์ง๋ 9์์ Java25 ๋ฒ์ ์ด ์ ์ ๋ฆด๋ฆฌ์ฆ ๋๋ค. ์ฌ๋ฌ๊ฐ์ง ๊ฐ์ ๊ณผ ๋ณํ๊ฐ ์์์ง๋ง ๊ทธ ์ค ๋์ ๋๋ ํญ๋ชฉ ์ค ํ๋๊ฐ ์์๋ค. ๋ฐ๋ก Compact Object Header ๊ธฐ๋ฅ์ผ๋ก ๊ฐ์ฒด์ ํค๋ ์ฌ์ด์ฆ๋ฅผ ๊ธฐ์กด 12~16byte์์ 8byte๋ก ์ค์๋ค๋ ๋ด์ฉ์ด๋ค. 8byte ๋ผ๋ ์์น๊ฐ ์๋นํ ์๊ฒ ๋๊ปด์ง ์ ์๊ฒ ์ง๋ง ์์ฒ, ์๋ง๊ฐ์ ๊ฐ์ฒด๋ค์ด ์์ฑ๋๋ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๊ฐ์ฒด๋น 4~8byte ์ ์ฝ์ ๋ฉ๋ชจ๋ฆฌ ์ ๊ฐ ํจ๊ณผ๋ฅผ ๊ธฐ๋ํ ์ ์๋ค. ์ด๋ฒ ํฌ์คํ ์์๋ JOL(Java Object Layer)๋ฅผ ์ฌ์ฉํ์ฌ ์ ๋ง ๊ฐ์ฒด์ ํค๋ ์ฌ์ด์ฆ๊ฐ ์ค์ด๋๋์ง ํ์ธํด ๋ณด์.
HotSpot JVM ์ด๋?
JVM์ ์๋ฐ๊ฐ ์ด๋ป๊ฒ ๋์๊ฐ์ผ ํ๋์ง ์ ํ ์ค๊ณ๋๋ผ๊ณ ํ๋ค๋ฉด HotSpot JVM์ ์ค์ ๋ก ๋ง๋ค์ด์ง ์ํํธ์จ์ด(๊ตฌํ์ฒด)๋ค. ์ค๋ผํด JDK๋ OpenJDK์์ HotSpot JVM์ ๊ธฐ๋ณธ ์์ง์ผ๋ก ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋ณดํต ์ฐ๋ฆฌ๊ฐ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉํ๋ ํ์ค JVM์ HotSpot JVM์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค. ๊ตฌํ์ฒด๋ก์จ HotSopt JVM์ด ์๋ ๋ค๋ฅธ ๊ตฌํ์ฒด๋ ์กด์ฌํ ๊ฒ์ด๋ค. GraalVM์ด๋ OpenJ9, Azul Platform Prime๊ณผ ๊ฐ์ ๋ค๋ฅธ JVM ๊ตฌํ์ฒด๋ ์ด ํฌ์คํ
์ ๋์์ด ์๋๋ค. ์ด ํฌ์คํ
์ ๋์์ HotSpot JVM์ ๋์์ผ๋ก ํ๋ค.
Compact Object Header ๊ธฐ๋ฅ ํ์คํ ๋ฆฌ
- JEP450 – Java24 ์คํ ๊ธฐ๋ฅ์ผ๋ก ์ฒ์ Compact Object Header๊ฐ ์ ์๋์๋ค. 64bit ์ํคํ ์ฒ์์ 96~128bit ํค๋ ์ฌ์ด์ฆ๋ฅผ 64bit ํฌ๊ธฐ๋ก ์ค์ด๊ธฐ ์ํ ๋ชฉ์ ์ด๋ค.
- JEP519 – Java25์์ ์ ์ ๊ธฐ๋ฅ์ผ๋ก ๋ฆด๋ฆฌ์ฆ ๋์๋ค.
Compact Object Header JVM Flag
Java25์์ Compact Object Header ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ ค๋ฉด JVM ํ๋๊ทธ๋ฅผ ์ง์ ํด ์ค์ผ ํ๋ค. ํ๋๊ทธ๋ฅผ ์ง์ ํ์ง ์์ผ๋ฉด Compact Object Header๊ฐ ์๋ํ์ง ์๋๋ค.
-XX:+UseCompactObjectHeadersJavaJava24 preview ํ๊ฒฝ์์๋ ๋ค์ ํ๋๊ทธ๋ฅผ ์ถ๊ฐ๋ก ์ง์ ํด ์ค์ผ ํ๋ค.
-XX:+UnlockExperimentalVMOptionsJavaJOL(Java Object Layout)์ ์ด์ฉํ ํ์ธ
JOL์ JVM ๋ด๋ถ๋ฅผ ๋ค์ฌ๋ค๋ณด๊ณ ๊ฐ์ฒด๊ฐ ๋ฉ๋ชจ๋ฆฌ์ ์ค์ ๋ก ์ด๋ป๊ฒ ๋ฐฐ์น๋๋์ง(ํค๋, ํ๋, ํจ๋ฉ๋ฑ) ๋ฐ์ดํธ ๋จ์๋ก ๋ณด์ฌ์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค. ์ํ ์ฝ๋์ JOL ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด์ ์ค์ ๋ก ๊ฐ์ฒด ํค๋ ํฌ๊ธฐ๊ฐ ์ค์ด๋๋์ง ํ์ธํด ๋ณด์.
JOL github์ ReadMe๋ฅผ ํตํด์ JOL์ ๋ํด์ ๋ ๋ง์ ์ ๋ณด๋ฅผ ์ป์ ์ ์๋ค.
์ํ ์ฝ๋
JOL ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ํ๋์๋ฅผ ์ถ๊ฐํ๋ค.
implementation 'org.openjdk.jol:jol-core:0.17'Groovy์ด์ ๊ฐ์ฒด ์์ฑ ํ JOL์ ํตํด์ ํ๋ฉด์ ์ฐ์ด์ฃผ๊ธฐ๋ง ํ๋ฉด ๋๋ค.
public class JolExample {
static class EmptyObject {}
static class SimpleObject {
int id;
boolean active;
}
public static void main( String[] args ) {
// ํ์ฌ JVM ํ๊ฒฝ ์ ๋ณด ์ถ๋ ฅ (์์ถ OOP ์ฌ์ฉ ์ฌ๋ถ ๋ฑ ํ์ธ)
System.out.println( VM.current().details());
System.out.println("======================================");
// 1. EmptyObject ๋ ์ด์์ ์ถ๋ ฅ
EmptyObject empty = new EmptyObject();
System.out.println("### 1. EmptyObject Layout ###");
// ClassLayout.parseInstance(๊ฐ์ฒด).toPrintable()์ด ํต์ฌ์
๋๋ค.
System.out.println( ClassLayout.parseInstance(empty).toPrintable());
System.out.println("======================================");
// 2. SimpleData ๋ ์ด์์ ์ถ๋ ฅ
SimpleObject data = new SimpleObject();
System.out.println("### 2. SimpleData Layout ###");
System.out.println(ClassLayout.parseInstance(data).toPrintable());
}
}Java๋น ๊ฐ์ฒด(EmptyObject)์ int, boolean ํ์ ์ ๊ฐ์ง ๊ฐ์ฒด(SimpleObject)๋ฅผ ์์ฑํ์ฌ JOL์ ํตํด์ ๋ฉ๋ชจ๋ฆฌ ๋ด์ฉ์ ์ถ๋ ฅํ ์ฝ๋๋ค.
Java21 ์์ ์คํ
Java21 ๋ฒ์ ์ผ๋ก main ํจ์๋ฅผ ์คํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ๊ฒฐ๊ณผ๊ฐ ์ถ๋ ฅ๋๋ค.
# VM mode: 64 bits
# Compressed references (oops): 3-bit shift
# Compressed class pointers: 3-bit shift
======================================
### 1. EmptyObject Layout ###
JolExample$EmptyObject object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0x0101ea50
12 4 (object alignment gap)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
======================================
### 2. SimpleData Layout ###
JolExample$SimpleObject object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0x0101fc00
12 4 int SimpleObject.id 0
16 1 boolean SimpleObject.active false
17 7 (object alignment gap)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes totalPlaintextEmptyObject๋ ๋น ๊ฐ์ฒด์ด๋ฏ๋ก ํค๋ ํฌ๊ธฐ 12byte + padding 4byte(object alignment gap)์ผ๋ก 16byte๋ค. ์ค์ ํค๋ ํฌ๊ธฐ๊ฐ header: mark, header: class ์ฌ์ด์ฆ๊ฐ 12byte์ธ๋ฐ ์ด์ ๋ Compressed Oops ๊ธฐ๋ฅ๋๋ฌธ์ผ๋ก ๊ธฐ๋ณธ์ ์ผ๋ก enable ์ํ์ธ๋ฐ ์ด ๊ธฐ๋ฅ์ด ํ์ฑํ๋๋ฉด class pointer๋ฅผ ์์ถํ์ฌ 8 -> 4byte๋ก ์ค์ฌ์ฃผ๋ ์ญํ ์ ํ๋ค. ์ด๋ฌ๋ ์ ๋ฌ๋ padding 4byte๊ฐ ์ถ๊ฐ๋๋ฏ๋ก 16byte๊ฐ ๋๋ค.
padding์ด ์ถ๊ฐ๋๋ ์ด์ ๋ JVM์ ์ ๋ ฌ ๊ท์น์ด ‘๋ชจ๋ ๊ฐ์ฒด ํฌ๊ธฐ๋ 8์ ๋ฐฐ์๋ก ๋๋์ผ ํ๋ค’๋ ๊ท์น์ด๊ธฐ ๋๋ฌธ์ด๋ค.
Java25 ์์ ์คํ
์์ง java 25 ๋ฒ์ ์ด ์ค์น๋์ด ์์ง ์๊ณ sdkman์ ์ฌ์ฉํ๋ค๋ฉด ์๋ ๋ช
๋ น์ผ๋ก ์ฝ๊ฒ ์ค์น ๊ฐ๋ฅํ๋ค.
sdkman ์ฌ์ฉ๋ฒ์ ์๋ ํฌ์คํ
์ ์ฐธ๊ณ ํ๊ธฐ ๋ฐ๋๋ค.
SDKMAN์ผ๋ก ๊ฐ๋ฐ๋๊ตฌ ๋ฒ์ ์ฝ๊ฒ ๊ด๋ฆฌํ์
sdk install java 25.0.1-temBash๋ง์ฝ java 25, gradle 8.14 ๋ฒ์ ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ
๋น๋๊ฐ ํ์ฌ ํธํ๋์ง ์๋ Java 25.0.1 ๋ฐ Gradle 8.14์(๋ฅผ) ์ฌ์ฉํ๋๋ก ๊ตฌ์ฑ๋์์ต๋๋ค. ํ๋ก์ ํธ๋ฅผ ๋๊ธฐํํ ์ ์์ต๋๋ค.
ํธํ๋๋ ์ต๋ Gradle JVM ๋ฒ์ ์ 24์
๋๋ค.Bash์ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค๋ฉด gradle์ ์คํํ๋ ์๋ฐ ๋ฒ์ ์ 24 ์ด์ ๋ฒ์ ์ผ๋ก ์ค์ ํด์ผ ํ๋ค.
IntelliJ๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ์ค์ > ๋น๋๋๊ตฌ > Gradle > Gradle JVM์์ java ๋ฒ์ ์ ์ง์ ํ ์ ์๋ค.
java 25 ๋ฒ์ ์์ main ํจ์๋ฅผ ์คํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ๊ฒฐ๊ณผ๊ฐ ์ถ๋ ฅ๋๋ค.
# VM mode: 64 bits
# Compressed references (oops): 3-bit shift
# Compressed class pointers: 3-bit shift
======================================
### 1. EmptyObject Layout ###
JolExample$EmptyObject object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0x01055ad8
12 4 (object alignment gap)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
======================================
### 2. SimpleData Layout ###
JolExample$SimpleObject object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0x010568b8
12 4 int SimpleObject.id 0
16 1 boolean SimpleObject.active false
17 7 (object alignment gap)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes totalPlaintextJava 21 ๋ฒ์ ์์ ์คํํ ๊ฒฐ๊ณผ์ ๋์ผํ๋ค. -XX:+UseCompactObjectHeaders ์ต์ ์ ์ง์ ํ์ง ์์๊ธฐ ๋๋ฌธ์ด๋ค.
Java25 ์์ ์คํ (-XX:+UseCompactObjectHeaders)
Java25 ์์ -XX:+UseCompactObjectHeaders ์ต์ ์ ์ง์ ํ๊ณ ์คํํ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ๋ค.
# VM mode: 64 bits
# Lilliput VM detected (experimental)
# Compressed references (oops): 3-bit shift
# Compressed class pointers: 3-bit shift
======================================
### 1. EmptyObject Layout ###
JolExample$EmptyObject object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x010c580000000001 (Lilliput)
Instance size: 8 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
======================================
### 2. SimpleData Layout ###
JolExample$SimpleObject object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x010cd00000000001 (Lilliput)
8 4 int SimpleObject.id 0
12 1 boolean SimpleObject.active false
13 3 (object alignment gap)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes totalPlaintext‘Lilliput VM detected (experimental)’ ์์ ํ์ธํ ์ ์๋ฏ์ด ์์ง ์คํ์ ๋จ๊ณ์ด์ง๋ง EmptyObject๋ header: mark๋ง ์กด์ฌํ๊ณ 8byte ์ฌ์ด์ฆ๋ก ์ค์ด๋ค์๋ค. (8byte๋ฏ๋ก padding ์ฒ๋ฆฌ๋ ์๋ค.)
SimpleObject ์ญ์ 24byte -> 16byte๋ก ์ฌ์ด์ฆ๊ฐ ์ค์ด๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
JEP 519์ ๋ฐ๋ฅด๋ฉด Compact Object Header ํ์ฑํ๊ฐ ์ฑ๋ฅ์ ํฅ์์ํจ๋ค๋ ์ฌ์ค์ด ์ ์ฆ๋์๋ค๊ณ ํ๋ค.
- ์ด๋ค ํ๊ฒฝ์์์ ํ ์คํธ๋ ํ ๊ณต๊ฐ 22% ์ ์ฝ CPU ์๊ฐ 8% ์ ์ฝ ํจ๊ณผ
- ๋ค๋ฅธ ํ๊ฒฝ์์์ ํ ์คํธ๋ GC ํ์๊ฐ G1 ๋ฐ Parallel ์ปฌ๋ ํฐ ๋ชจ๋ 15% ๊ฐ์ ํจ๊ณผ