Java25 Compact Object Header๋กœ HotSpot JVM์—์„œ ๋ฉ”๋ชจ๋ฆฌ ์ ˆ์•ฝ

์ง€๋‚œ 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:+UseCompactObjectHeaders
Java

Java24 preview ํ™˜๊ฒฝ์—์„œ๋Š” ๋‹ค์Œ ํ”Œ๋ž˜๊ทธ๋ฅผ ์ถ”๊ฐ€๋กœ ์ง€์ •ํ•ด ์ค˜์•ผ ํ•œ๋‹ค.

-XX:+UnlockExperimentalVMOptions
Java

JOL(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 total
Plaintext

EmptyObject๋Š” ๋นˆ ๊ฐ์ฒด์ด๋ฏ€๋กœ ํ—ค๋” ํฌ๊ธฐ 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-tem
Bash

๋งŒ์•ฝ 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 total
Plaintext

Java 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 total
Plaintext

‘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% ๊ฐ์†Œ ํšจ๊ณผ