# Java New Features Java 9 부터 코딩 스타일 위주로 변경된 내용을 기술한다. Preview 는 제외했고 정식 기능으로 들어간 것 위주라 실질적으로 몇몇 버전이 빠져있다. 코딩 스타일 외에 가장 큰 변화는 Java 21 의 Virtual Thread 를 들 수 있다. 기존의 Java thread 가 OS 의 thread 와 1:1 매핑이었다면, Virtual thread 는 go 의 coroutine 처럼 software thread 라고 할 수 있다. 많은 경우 그렇지만 virtual thread 도 만능은 아니다. 즉, 기존 thread 에서 세분화해야 하는 경우만 도입이 고려되지 그렇지 않은 경우는 software 로 OS thread 매핑이 한 번 더 되는 꼴이라 딱히 성능 이득을 볼 수 없을 수 있다. # JDK 10 ## 로컬 변수 타입 유추 아래와 같이 var 로 선언하고 변수에 타입을 유추하는 형태 ```java var list = new ArrayList<String>(); // infers ArrayList<String> var stream = list.stream(); // infers Stream<String> ``` # JDK 11 ## 로컬 변수 Lamba 암묵적인 타입 선언으로 람다를 구현하는 경우 ```java (var x, var y) -> x.process(y) ``` 동일 형태 ```java (x, y) -> x.process(y) ``` 섞어서는 못 쓴다. ```java (var x, y) -> x.process(y) // Cannot mix 'var' and 'no var' in implicitly typed lambda expression (var x, int y) -> x.process(y) // Cannot mix 'var' and manifest types in explicitly typed lambda expression ``` # JDK 14 ## Switch 구문 축약해서 사용 가능 ```java switch (day) { case MONDAY, FRIDAY, SUNDAY -> System.out.println(6); case TUESDAY -> System.out.println(7); case THURSDAY, SATURDAY -> System.out.println(8); case WEDNESDAY -> System.out.println(9); } ``` 변수 대입도 가능 ```java int numLetters = switch (day) { case MONDAY, FRIDAY, SUNDAY -> 6; case TUESDAY -> 7; case THURSDAY, SATURDAY -> 8; case WEDNESDAY -> 9; }; ``` # JDK 15 ## Text Block """ 로 시작해서 """ 로 끝나는 블록 구문이다. 기존 구문이 이렇다면 ```java String html = "<html>\n" + " <body>\n" + " <p>Hello, world</p>\n" + " </body>\n" + "</html>\n"; ``` 이렇게 가능하다. ```java String html = """ <html> <body> <p>Hello, world</p> </body> </html> """; ``` # JDK 16 ## Pattern Matching for instanceof 기존에 이렇게 쓰던 것을 ```java if (obj instanceof String) { String s = (String) obj; // grr… … } ``` 이렇게 사용할 수 있다. ```java if (obj instanceof String s) { // Let pattern matching do the work! … } ``` 근데 이게 Scope 가 묘한게 if 블럭 안에서만 유효하다. 예를 들어 ```java class Example1 { String s; void test1(Object o) { if (o instanceof String s) { System.out.println(s); // Field s is shadowed s = s + "\n"; // Assignment to pattern variable … } System.out.println(s); // Refers to field s … } } ``` 심지어 else 구문에서도 이게 유효하지 않다. ``` class Example2 { Point p; void test2(Object o) { if (o instanceof Point p) { // p refers to the pattern variable … } else { // p refers to the field … } } } ``` ## Records 데이터 구조체를 간단하게 구현한 것이다. 예를 들어 전통적인 방식의 데이터 구조체를 class 로 구현한 것이 다음과 같다면 ```java class Point { private final int x; private final int y; Point(int x, int y) { this.x = x; this.y = y; } int x() { return x; } int y() { return y; } public boolean equals(Object o) { if (!(o instanceof Point)) return false; Point other = (Point) o; return other.x == x && other.y == y; } public int hashCode() { return Objects.hash(x, y); } public String toString() { return String.format("Point[x=%d, y=%d]", x, y); } } ``` 이 기능을 다 하는 record 는 다음과 같이 정의한다. ```java record Point(int x, int y) { } ``` 단, 이렇게 보면 좋아보이지만 이 x, y 는 초기에 값을 대입받으면 더 이상 수정할 수 없다. ### Constructor Constructor 에서 추가 작업을 한다면 다음과 같이 사용할 수 있다. ```java record Rational(int num, int denom) { Rational { int gcd = gcd(num, denom); num /= gcd; denom /= gcd; } } ``` 동일 코드를 명시적으로 작성하면 다음과 같다. ```java record Rational(int num, int denom) { Rational(int num, int demon) { // Normalization int gcd = gcd(num, denom); num /= gcd; denom /= gcd; // Initialization this.num = num; this.denom = denom; } } ``` Exception 처리 Case ``` java record Range(int lo, int hi) { Range { if (lo > hi) // referring here to the implicit constructor parameters throw new IllegalArgumentException(String.format("(%d,%d)", lo, hi)); } } ``` Copy 는 다음과 같이 사용한다. 아래와 같은 record 가 있다고 하면 ```java record R(T1 c1, ..., Tn cn){ } ``` r1 인스턴스가 먼저 있고 이게 null 이 아니면 ```java R r2 = new R(r1.c1(), r1.c2(), ..., r1.cn()); ``` r2 가 r1의 복제이다. 이 경우 r1.equals(r2) 는 true 이다. ### Rules for record classes - 상속 불가. record class 의 부모는 모두 java.lang.Record 이다. sealed class 를 implements 는 가능하다. ```java package com.example.expression; public sealed interface Expr permits ConstantExpr, PlusExpr, TimesExpr, NegExpr {...} public record ConstantExpr(int i) implements Expr {...} public record PlusExpr(Expr a, Expr b) implements Expr {...} public record TimesExpr(Expr a, Expr b) implements Expr {...} public record NegExpr(Expr e) implements Expr {...} ``` - record class 는 암묵적으로 final 이고 abstract 가 되지 못한다. - 필드들도 모두 final 이다. - 인스턴스 필드를 명시적으로 정의할 수 없고 필드를 초기화할 수 없다. ```java record Invalid(int x, inty) { int z; // 인스턴스 필드 명시화 안됨. int a = 1; // 명시화도 안되고 초기화도 안됨. } ``` # JDK 17 ## Sealed Classes Sealed class 나 interface 는 상속 받을 대상을 명시한다. 패키지 내부인 경우 ```java package com.example.geometry; public abstract sealed class Shape permits Circle, Rectangle, Square { ... } ``` 다른 패키지인 경우 ```java package com.example.geometry; public abstract sealed class Shape permits com.example.polar.Circle, com.example.quad.Rectangle, com.example.quad.simple.Square { ... } ``` 패턴 매칭에 사용 가능하다. ```java Shape rotate(Shape shape, double angle) { return switch (shape) { // pattern matching switch case Circle c -> c; case Rectangle r -> shape.rotate(angle); case Square s -> shape.rotate(angle); // no default needed! } } ``` # JDK 18 ## Code Snippets in Java API Documentation @snippet 태그 사용 ```java /** * The following code shows how to use {@code Optional.isPresent}: * {@snippet : * if (v.isPresent()) { * System.out.println("v: " + v.get()); * } * } */ ``` 실제 코드를 당겨오는 것도 가능 ```java /** * The following code shows how to use {@code Optional.isPresent}: * {@snippet file="ShowOptional.java" region="example"} */ ``` ShowOptional.java 의 내용에 region 을 정의한다. ```java public class ShowOptional { void show(Optional<String> v) { // @start region="example" if (v.isPresent()) { System.out.println("v: " + v.get()); } // @end } } ``` 그 밖에 강조를 하거나 문자열 대치, 링크 등을 사용할 수 있다. ## Deprecate Finalization for Removal 다음의 코드 대신에... ```java FileInputStream input = null; FileOutputStream output = null; try { input = new FileInputStream(file1); output = new FileOutputStream(file2); ... copy bytes from input to output … output.close(); output = null; input.close(); input = null; } finally { if (output != null) output.close(); if (input != null) input.close(); } ``` try-resource-statement 와 cleaners 를 사용할 것. try-resource-statement 는 Java 7 에 도입됨. ```java try (FileInputStream input = new FileInputStream(file1); FileOutputStream output = new FileOutputStream(file2)) { ... copy bytes from input to output … } ``` cleaner 는 Java 9 에 도입됨. (interface 구하는 방식) # JDK 21 ## Sequenced Collections first 와 last 에 대한 명시적인 method 를 제공한다. #### Sequenced collections ```java interface SequencedCollection<E> extends Collection<E> { // new method SequencedCollection<E> reversed(); // methods promoted from Deque void addFirst(E); void addLast(E); E getFirst(); E getLast(); E removeFirst(); E removeLast(); } ``` ### Sequenced sets ```java interface SequencedSet<E> extends Set<E>, SequencedCollection<E> { SequencedSet<E> reversed(); // covariant override } ``` ### Sequenced maps ```java interface SequencedMap<K,V> extends Map<K,V> { // new methods SequencedMap<K,V> reversed(); SequencedSet<K> sequencedKeySet(); SequencedCollection<V> sequencedValues(); SequencedSet<Entry<K,V>> sequencedEntrySet(); V putFirst(K, V); V putLast(K, V); // methods promoted from NavigableMap Entry<K, V> firstEntry(); Entry<K, V> lastEntry(); Entry<K, V> pollFirstEntry(); Entry<K, V> pollLastEntry(); } ``` ### Retrofitting 그래서 다음과 같이 재조정된다. - List now has SequencedCollection as its immediate superinterface, - Deque now has SequencedCollection as its immediate superinterface, - LinkedHashSet additionally implements SequencedSet, - SortedSet now has SequencedSet as its immediate superinterface, - LinkedHashMap additionally implements SequencedMap, and - SortedMap now has SequencedMap as its immediate superinterface. ## Record Patterns JDK 16 의 Pattern Matching for instanceof 에 Record 확장판 JDK 16 당시는 다음과 같이 사용 ```java record Point(int x, int y) {} static void printSum(Object obj) { if (obj instanceof Point p) { int x = p.x(); int y = p.y(); System.out.println(x+y); } } ``` 이걸 JDK 21에서는 다음과 같이 사용 가능하다. ```java static void printSum(Object obj) { if (obj instanceof Point(int x, int y)) { System.out.println(x+y); } } ``` 이 때 Point(int x, inty) 를 record pattern 이라고 한다. ### Nested record patterns 다단계인 경우 다음과 같이 사용 가능하다. ```java record Point(int x, int y) {} enum Color { RED, GREEN, BLUE } record ColoredPoint(Point p, Color c) {} record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {} ``` 1단계 ```java static void printUpperLeftColoredPoint(Rectangle r) { if (r instanceof Rectangle(ColoredPoint ul, ColoredPoint lr)) { System.out.println(ul.c()); } } ``` 2단계 ```java static void printColorOfUpperLeftPoint(Rectangle r) { if (r instanceof Rectangle(ColoredPoint(Point p, Color c), ColoredPoint lr)) { System.out.println(c); } } ``` var 사용 예제 ```java Rectangle r = new Rectangle(new ColoredPoint(new Point(x1, y1), c1), new ColoredPoint(new Point(x2, y2), c2)); static void printXCoordOfUpperLeftPointWithPatterns(Rectangle r) { if (r instanceof Rectangle(ColoredPoint(Point(var x, var y), var c), var lr)) { System.out.println("Upper-left corner: " + x); } } ``` ## Pattern Matching for switch ```java static String formatter(Object obj) { String formatted = "unknown"; if (obj instanceof Integer i) { formatted = String.format("int %d", i); } else if (obj instanceof Long l) { formatted = String.format("long %d", l); } else if (obj instanceof Double d) { formatted = String.format("double %f", d); } else if (obj instanceof String s) { formatted = String.format("String %s", s); } return formatted; } ``` 이랬던 것을 아래와 같이 사용 가능하다. ```java static String formatterPatternSwitch(Object obj) { return switch (obj) { case Integer i -> String.format("int %d", i); case Long l -> String.format("long %d", l); case Double d -> String.format("double %f", d); case String s -> String.format("String %s", s); default -> obj.toString(); }; } ``` ### switch 에 null before ```java static void testFooBarOld(String s) { if (s == null) { System.out.println("Oops!"); return; } switch (s) { case "Foo", "Bar" -> System.out.println("Great"); default -> System.out.println("Ok"); } } ``` after ```java static void testFooBarNew(String s) { switch (s) { case null -> System.out.println("Oops"); case "Foo", "Bar" -> System.out.println("Great"); default -> System.out.println("Ok"); } } ``` ### Case refinement 좀 더 나가서 (아래도 JDK 21 부터 가능) ```java static void testStringOld(String response) { switch (response) { case null -> { } case String s -> { if (s.equalsIgnoreCase("YES")) System.out.println("You got it"); else if (s.equalsIgnoreCase("NO")) System.out.println("Shame"); else System.out.println("Sorry?"); } } } ``` 여러 가지로 가능하다. ```java static void testStringNew(String response) { switch (response) { case null -> { } case String s when s.equalsIgnoreCase("YES") -> { System.out.println("You got it"); } case String s when s.equalsIgnoreCase("NO") -> { System.out.println("Shame"); } case String s -> { System.out.println("Sorry?"); } } } ``` 좀 더 늘려서... ```java static void testStringEnhanced(String response) { switch (response) { case null -> { } case "y", "Y" -> { System.out.println("You got it"); } case "n", "N" -> { System.out.println("Shame"); } case String s when s.equalsIgnoreCase("YES") -> { System.out.println("You got it"); } case String s when s.equalsIgnoreCase("NO") -> { System.out.println("Shame"); } case String s -> { System.out.println("Sorry?"); } } } ``` ### enum 쓰는 경우 before ```java public enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES } static void testforHearts(Suit s) { switch (s) { case HEARTS -> System.out.println("It's a heart!"); default -> System.out.println("Some other suit"); } } ``` after (sealed 도 같이 사용) ```java sealed interface CardClassification permits Suit, Tarot {} public enum Suit implements CardClassification { CLUBS, DIAMONDS, HEARTS, SPADES } final class Tarot implements CardClassification {} static void exhaustiveSwitchWithoutEnumSupport(CardClassification c) { switch (c) { case Suit s when s == Suit.CLUBS -> { System.out.println("It's clubs"); } case Suit s when s == Suit.DIAMONDS -> { System.out.println("It's diamonds"); } case Suit s when s == Suit.HEARTS -> { System.out.println("It's hearts"); } case Suit s -> { System.out.println("It's spades"); } case Tarot t -> { System.out.println("It's a tarot"); } } } ``` 이렇게도 가능하다. (이게 좀 더 명시적인 듯...) ```java static void exhaustiveSwitchWithBetterEnumSupport(CardClassification c) { switch (c) { case Suit.CLUBS -> { System.out.println("It's clubs"); } case Suit.DIAMONDS -> { System.out.println("It's diamonds"); } case Suit.HEARTS -> { System.out.println("It's hearts"); } case Suit.SPADES -> { System.out.println("It's spades"); } case Tarot t -> { System.out.println("It's a tarot"); } } } ``` # JDK 22 ## Foreign Function & Memory API https://openjdk.org/jeps/454 이건 Coding Style 보다는 기능의 대체이다. JNI 를 대체하기 위해 나왔고 조금 복잡해 보이긴 한데, JNI 도 복잡했으니 비슷하지 않을까 싶다. ### 목적 Java runtime 바깥의 코드와 데이터에 대한 접근을 위한 것인데 JNI 의 단점을 보완하려고 한다. * JNI의 native 메소드를 pure-java API 로 대체한다. * JNI 보다는 오버헤드가 있을 듯 함. * JVM 이 실행되는 모든 플랫폼의 native library 의 탐색 및 수행 * 다양한 (native, persistent, managed heap) 메모리 접근에 대한 방법을 제공함 * thread 간의 메모리 할당과 해지 시에도 쓰지 않는 메모리에 대한 해지 버그에 대해 보장 * native code 와 data 에 대한 안전하지 않은 (unsafe) 동작에 대해 허용하되 이에 대해 사용자에게 경고하는 기능 ### Foreign memory (before) 객체들은 heap memory 에 만들어지고 사용이 끝나면 GC 로 사라진다. heap memory 말고 다른 곳에 만들어지는 off-heap memory 가 필요한 경우를 지원. 몰랐는데 이전에도 off-heap 메모리를 사용했다고... - ByteBuffer API 에서 시스템의 native I/O 에서 직접 access 할 수 있도록 하기 위해서 allocateDirect 로 할당 시 off-heap memory 로 관리. 주로 Non-block 이나 Async I/O 구현 하는 netty 같은 곳에서 사용. - sun.misc.Unsafe API 를 사용하는 경우 -> 잘 안 썼던 것 같다. ### Foreign functions (before) JNI 로 지원 ### 그래서 FFM API 는 다음의 클래스와 인터페이스를 추가함. - foreign memory 에 대한 allocation 과 deallocation 제어 - MemorySegment - Arena - SegmentAllocator - 구조화된 foreign memory 접근 및 조작 - MemoryLayout - VarHandle - foreign functions 호출 - Linker - SymbolLockup - FunctionDescriptor - MethodHandle ## Unnamed Variables & Patterns 필요에 의해 정의는 하지만 실제로 쓰이지 않는 경우를 대비해서 만들어진 기능이다. for loop 를 위해 정의는 하지만 실제로 필요하지 않은 경우 ```java static int count(Iterable<Order> orders) { int total = 0; for (Order order : orders) // order is unused total++; return total; } ``` 다음과 같이 사용할 수 있다. ```java static int count(Iterable<Order> orders) { int total = 0; for (Order _ : orders) // Unnamed variable total++; return total; } ``` 아래의 경우에도 z 대신에 _ 를 사용할 수 있다. ```java Queue<Integer> q = ... // x1, y1, z1, x2, y2, z2 .. while (q.size() >= 3) { int x = q.remove(); int y = q.remove(); int z = q.remove(); // z is unused ... new Point(x, y) ... } ``` try catch 구문에서 catch 한 Exception 을 만나서 실제로 쓰지 않는 경우에도 Exception 변수로 _ 사용 가능. 패턴의 경우에도 유사. ```java sealed abstract class Ball permits RedBall, BlueBall, GreenBall { } final class RedBall extends Ball { } final class BlueBall extends Ball { } final class GreenBall extends Ball { } Ball ball = ... switch (ball) { case RedBall red -> process(ball); case BlueBall blue -> process(ball); case GreenBall green -> stopProcessing(); } ``` 위와 같이 사용하는 경우 red, blue, greean 은 쓰이지 않으므로 다음과 같이 변경할 수 있다. ```java switch (ball) { case RedBall _ -> process(ball); // Unnamed pattern variable case BlueBall _ -> process(ball); // Unnamed pattern variable case GreenBall _ -> stopProcessing(); // Unnamed pattern variable } ``` > [!Tip] Java 파일 수행 > JDK 22 에 Launch Multi-File Source-Code Programs 라는 항목이 있다. 그리고 JDK 11 에서 java Prog.java 형태로 소스코드 파일을 바로 실행할 수 있었다고... 이제는 멀티가 가능하다고 한다. 자세한 내용은 https://openjdk.org/jeps/458 참고. # JDK 23 ## Markdown Documentation Comments 기존에 html 태그를 넣어서 API 문서 작성하던 것을 Markdown 도 쓸 수 있게 함. ```java /** * Returns a hash code value for the object. This method is * supported for the benefit of hash tables such as those provided by * {@link java.util.HashMap}. * <p> * The general contract of {@code hashCode} is: * <ul> * <li>Whenever it is invoked on the same object more than once during * an execution of a Java application, the {@code hashCode} method * must consistently return the same integer, provided no information * used in {@code equals} comparisons on the object is modified. * This integer need not remain consistent from one execution of an * application to another execution of the same application. * <li>If two objects are equal according to the {@link * #equals(Object) equals} method, then calling the {@code * hashCode} method on each of the two objects must produce the * same integer result. * <li>It is <em>not</em> required that if two objects are unequal * according to the {@link #equals(Object) equals} method, then * calling the {@code hashCode} method on each of the two objects * must produce distinct integer results. However, the programmer * should be aware that producing distinct integer results for * unequal objects may improve the performance of hash tables. * </ul> * * @implSpec * As far as is reasonably practical, the {@code hashCode} method defined * by class {@code Object} returns distinct integers for distinct objects. * * @return a hash code value for this object. * @see java.lang.Object#equals(java.lang.Object) * @see java.lang.System#identityHashCode */ ``` Markdown 은 /// 로 구분한다. ```java /// Returns a hash code value for the object. This method is /// supported for the benefit of hash tables such as those provided by /// [java.util.HashMap]. /// /// The general contract of `hashCode` is: /// /// - Whenever it is invoked on the same object more than once during /// an execution of a Java application, the `hashCode` method /// must consistently return the same integer, provided no information /// used in `equals` comparisons on the object is modified. /// This integer need not remain consistent from one execution of an /// application to another execution of the same application. /// - If two objects are equal according to the /// [equals][#equals(Object)] method, then calling the /// `hashCode` method on each of the two objects must produce the /// same integer result. /// - It is _not_ required that if two objects are unequal /// according to the [equals][#equals(Object)] method, then /// calling the `hashCode` method on each of the two objects /// must produce distinct integer results. However, the programmer /// should be aware that producing distinct integer results for /// unequal objects may improve the performance of hash tables. /// /// @implSpec /// As far as is reasonably practical, the `hashCode` method defined /// by class `Object` returns distinct integers for distinct objects. /// /// @return a hash code value for this object. /// @see java.lang.Object#equals(java.lang.Object) /// @see java.lang.System#identityHashCode ``` table 가능하고 JavaDoc 태그는 그냥 써야 하는 듯 하다. 코드 블럭도 가능하다. # JDK 24 대부분 성능 관련 개선이나 API 개선에 대한 내용들이다. # JDK 25 ## Scoped Values Virtual Thread 를 지원하기 위해 Thread 내에서 값을 공유할 수 있다. ```java public class Framework { private final Application application; public Framework(Application app) { this.application = app; } private static final ThreadLocal<FrameworkContext> CONTEXT = new ThreadLocal<>(); // (1) void serve(Request request, Response response) { var context = createContext(request); CONTEXT.set(context); // (2) Application.handle(request, response); } public PersistedObject readKey(String key) { var context = CONTEXT.get(); // (3) var db = getDBConnection(context); db.readKey(key); } } ``` Thread 간의 value 공유도 가능한 듯 한데, 이 부분은 structured concurrency(현재는 preview) 에서 가능한 듯 하다. ## Key Derivation Function API HKDF(RFC 5869)나 Argon2 (RFC 9106) 같은 키 도출 함수 지원. ```java // Create a KDF object for the specified algorithm KDF hkdf = KDF.getInstance("HKDF-SHA256"); // Create an ExtractExpand parameter specification AlgorithmParameterSpec params = HKDFParameterSpec.ofExtract() .addIKM(initialKeyMaterial) .addSalt(salt).thenExpand(info, 32); // Derive a 32-byte AES key SecretKey key = hkdf.deriveKey("AES", params); // Additional deriveKey calls can be made with the same KDF object ``` ## Module Import Declarations import module 문으로 다양한 패키지를 모듈 단위로 import 한다. 예를 들어 아래와 같은 예는 ```java import javax.xml.*; import javax.xml.parsers.*; import javax.xml.stream.*; ``` 다음과 같이 바꿀 수 있다. 이는 계층 구조 전체를 가져올 수 있다는 뜻. ```java import module java.xml; ``` ## Compact Source Files and Instance Main Methods 이 소스코드를... ```java public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } } ``` 이렇게 줄일 수 있다고... ```java void main() { IO.println("Hello, World!"); } ``` ```PowerShell PS C:\Users\bruckner\Workspace\T2X> cat .\HelloWorld.java void main() { IO.println("Hello, World!"); } PS C:\Users\bruckner\Workspace\T2X> java .\HelloWorld.java Hello, World! PS C:\Users\bruckner\Workspace\T2X> java -version openjdk version "25.0.1" 2025-10-21 LTS OpenJDK Runtime Environment Temurin-25.0.1+8 (build 25.0.1+8-LTS) OpenJDK 64-Bit Server VM Temurin-25.0.1+8 (build 25.0.1+8-LTS, mixed mode, sharing) ``` 되네요. ## Flexible Constructor Bodies JDK 25 중 가장 중요한 변화. 상속 받은 Class 생성 시에 Super Class 먼저 생성하는 방식에 대한 변경이다. 이전에 이런 방식으로 밖에 짤 수 없었다. ```java class Object { Object() { // Object constructor body } } class A extends Object { A() { super(); // A constructor body } } class B extends A { B() { super(); // B constructor body } } class C extends B { C() { super(); // C constructor body } } class D extends C { D() { super(); // D constructor body } } ``` 호출하면 다음과 같이 된다. ``` D --> C --> B --> A --> Object constructor body --> A constructor body --> B constructor body --> C constructor body D constructor body ``` 변경된 방식은 다음과 같다. ```java class Object { Object() { // Object constructor body } } class A extends Object { A() { // A prologue super(); // A epilogue } } class B extends A { B() { // B prologue super(); // B epilogue } } class C extends B { C() { // C prologue super(); // C epilogue } } class D extends C { D() { // D prologue super(); // D epilogue } } ``` 호출 순서는 다음과 같다. ``` D prologue --> C prologue --> B prologue --> A prologue --> Object constructor body --> A epilogue --> B epilogue --> C epilogue D epilogue ``` 생각해보면 쉬운 일 같은데 지금까지 안해준 것도 신기하고... 물론 제약 사항도 있는데 super 를 호출하기 전 까지는 인스턴스화가 완전하게 된 것이 아니어서 다음과 같이 오류가 날 수 있다. ```java class X { int i; String s = "hello"; X() { System.out.print(this); // Error - explicitly refers to the current instance var x = this.i; // Error - explicitly refers to field of the current instance this.hashCode(); // Error - explicitly refers to method of the current instance var y = i; // Error - implicitly refers to field of the current instance hashCode(); // Error - implicitly refers to method of the current instance i = 42; // OK - assignment to an uninitialized declared field s = "goodbye"; // Error - assignment to an initialized declared field super(); } } ``` # Garbage Collector 중간 정리 | 종류 | Option | 동작 방식 | 장점 | 단점 | | ----------------- | -------------------------------------- | ---------------- | -------------------- | ---------------------------------- | | Serial | -XX:+UseSerialGC | 싱글 스레드로 동작 | CPU 자원이 적을 때 효과적 | 멀티 스레드에서 쥐약. GC 동작 시 얼음 현상 | | Parallel | -XX:+UseParallelGC | 멀티 스레드로 동작 | 가장 많은 Garbage 정리는 가능 | GC 동작 시 얼음 현상 | | G1(Garbage First) | -XX:+UseG1GC | 파티션 구조 | 낮은 지연 시간 | 메모리 오버헤드 | | Shenandoah | -XX:+UseShenandoahGC | 파티션 구조, 잦은 GC 수행 | Z 대비 처리 시간이 김 | Z 대비 Garbage 처리량이 높음 | | Z | +XX:+UseZGC<br>+XX:+UseGenerationalZGC | 동적 파티션 구조 | 셰넌도 대비 처리 시간이 짧음 | 오리지널은 셰넌도 대비 처리량이 낮았으나 Z Gen 에서 극복 | 일단 SerialGC 가 먼저 나왔고 ParallelGC 는 JDK 8 에서 default 가 되었다. 이외에 CMS GC 라는 것도 있었는데 JDK 9 이후 deprecated 되고 JDK 14 에서 제거되었다. 이후 G1GC 가 JDK7 에 도입되고 JDK9에 default 가 된다. ZGC 는 JDK 15 에, GenerationalZGC 는 JDK 21 부터 지원한다. Shenandoah GC 는 JDK 17 부터 지원한다. (지원의 의미는 안정화.) Generational Shenandoah 는 experimental 로 JDK25 부터 도입. (안정화는 기다려야 할 듯) G1이 JDK 9 이후 현재 Default 이며 (LTS 로 보면 11, 17, 21, 25) G1 보다 저지연 및 메모리 오버헤드를 줄인 GC 로 만들고 있는 것이 Z 하고 Shenandoah 라고 보면 되는데 전반적으로 Z 가 조금 앞서는 느낌이다.
