Java OpenResty Spring Spring Boot MySQL Redis MongoDB PostgreSQL Linux Android Nginx 面试 小程序 Arthas JVM AQS juc Kubernetes Docker DevOps


JVM:开启标量替换后仍有部分对象在堆中生成

Java JVM 大约 4862 字

标量替换

将普通对象中的字段拆解为基本数据类型和字符串,并且直接讲这些字段存放在线程栈中的局部变量表中,使得不用在堆中开辟空间初始化对象。避免了在堆中竞争锁去申请空间new对象。

测试代码

public class OnStackTest {
    public static void alloc() {
        User u = new User();
        u.id = 5;
        u.name = "test";
    }
    public static void main(String[] args) throws InterruptedException {
        System.out.println("start visual vm...");
        long b = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            alloc();
        }
        System.out.println("loop end...");
        long e = System.currentTimeMillis();
        System.out.println(e - b);
        LockSupport.park();
    }
}

public class User {
    public int id = 0;

    public String name = "";
}

问题

在使用VisualVM观察,虽然开启了EliminateAllocations标量替换,禁用了TLAB本地线程分配缓冲,但还是有部分对象在堆中生成了。

原因

EliminateAllocations

JVM默认启用了分层编译器,标量替换EliminateAllocations是只对于C2编译器有效。

C1编译器默认在收集1500次方法调用后开启优化编译。

C2编译器默认在收集10000次方法调用后开启优化编译。

-XX:-TieredCompilation

关闭分层编译器,只有HotSpot Server虚拟机支持次命令,故需要与-server指令一起使用(对于默认是server的虚拟机可以不加,使用java -version可查看虚拟机是哪种模式)

-Xcomp

强制方法在第一次调用时就进行编译优化。

-XX:CompileThreshold

开启优化编译的阈值,尽可能的在这个阈值内完成优化编译,一般都会大于这个值。

如果设置的过小,比如设置为1,则会发现C2编译器没办法收集到优化信息导致不进行优化。

设置为0,则表示不启用C2编译器。

必须在关闭分层编译器的情况下生效,即设置-XX:-TieredCompilation

-XX:CompileThreshold 与 -Xcomp

-XX:CompileThreshold-Xcomp指令一起使用,则可以精确控制在方法调用几次后开启优化编译。

如:-XX:CompileThreshold=1 -Xcomp,表示方法调用一次后达到触发优化编译的条件,可以看见在堆上分配了2次,强制第一次调用时生成了一个,再调用一次记录为阈值(个人认为-Xcomp调用的这次不记录在CompileThreshold中),使用VisualVM可以见到有2Live Objects

-XX:+PrintCompilation

打印编译信息

-XX:CompileCommand="print,OnStackTest::alloc"

OnStackTestalloc方法的编译信息,可以看到是使用C2编译器。

分层编译器

默认为分层编译器。

Compiled method (c1)     318  203       3       OnStackTest::alloc (20 bytes)
 total in heap  [0x0000023e0c345190,0x0000023e0c345790] = 1536
 relocation     [0x0000023e0c345308,0x0000023e0c345358] = 80
 main code      [0x0000023e0c345360,0x0000023e0c3455e0] = 640
 stub code      [0x0000023e0c3455e0,0x0000023e0c345678] = 152
 oops           [0x0000023e0c345678,0x0000023e0c345680] = 8
 metadata       [0x0000023e0c345680,0x0000023e0c345698] = 24
 scopes data    [0x0000023e0c345698,0x0000023e0c3456e8] = 80
 scopes pcs     [0x0000023e0c3456e8,0x0000023e0c345788] = 160
 dependencies   [0x0000023e0c345788,0x0000023e0c345790] = 8
Could not load hsdis-amd64.dll; library not loadable; PrintAssembly is disabled

ImmutableOopMap{}pc offsets: 389 402 
ImmutableOopMap{rax=Oop }pc offsets: 435 468 Compiled method (c2)     320  205       4       OnStackTest::alloc (20 bytes)
 total in heap  [0x0000023e13841510,0x0000023e13841728] = 536
 relocation     [0x0000023e13841688,0x0000023e13841690] = 8
 main code      [0x0000023e138416a0,0x0000023e138416c0] = 32
 stub code      [0x0000023e138416c0,0x0000023e138416d8] = 24
 oops           [0x0000023e138416d8,0x0000023e138416e0] = 8
 metadata       [0x0000023e138416e0,0x0000023e138416e8] = 8
 scopes data    [0x0000023e138416e8,0x0000023e138416f0] = 8
 scopes pcs     [0x0000023e138416f0,0x0000023e13841720] = 48
 dependencies   [0x0000023e13841720,0x0000023e13841728] = 8

C2 编译器

参数:-XX:-TieredCompilation -server

Compiled method (c2)    9609 3337             OnStackTest::alloc (20 bytes)
 total in heap  [0x00000299adf3ca10,0x00000299adf3cc58] = 584
 relocation     [0x00000299adf3cb88,0x00000299adf3cb90] = 8
 main code      [0x00000299adf3cba0,0x00000299adf3cbc0] = 32
 stub code      [0x00000299adf3cbc0,0x00000299adf3cbd8] = 24
 oops           [0x00000299adf3cbd8,0x00000299adf3cbe0] = 8
 metadata       [0x00000299adf3cbe0,0x00000299adf3cbe8] = 8
 scopes data    [0x00000299adf3cbe8,0x00000299adf3cc00] = 24
 scopes pcs     [0x00000299adf3cc00,0x00000299adf3cc50] = 80
 dependencies   [0x00000299adf3cc50,0x00000299adf3cc58] = 8
Could not load hsdis-amd64.dll; library not loadable; PrintAssembly is disabled

ImmutableOopMap{}pc offsets: 24 Compiled method (c2)    9664 3353             OnStackTest::alloc (20 bytes)
 total in heap  [0x00000299adf3ed90,0x00000299adf3efa8] = 536
 relocation     [0x00000299adf3ef08,0x00000299adf3ef10] = 8
 main code      [0x00000299adf3ef20,0x00000299adf3ef40] = 32
 stub code      [0x00000299adf3ef40,0x00000299adf3ef58] = 24
 oops           [0x00000299adf3ef58,0x00000299adf3ef60] = 8
 metadata       [0x00000299adf3ef60,0x00000299adf3ef68] = 8
 scopes data    [0x00000299adf3ef68,0x00000299adf3ef70] = 8
 scopes pcs     [0x00000299adf3ef70,0x00000299adf3efa0] = 48
 dependencies   [0x00000299adf3efa0,0x00000299adf3efa8] = 8

相关参数

-Xmx1g -Xms1g -XX:+DoEscapeAnalysis -XX:+PrintGCDetails -XX:-UseTLAB -XX:+EliminateAllocations -XX:+PrintCompilation -XX:CompileCommand="print,OnStackTest::alloc" -XX:-TieredCompilation -server -XX:CompileThreshold=1 -Xcomp

参考

https://stackoverflow.com/questions/64739969/why-jvm-xxeliminateallocations-fail

https://docs.oracle.com/en/java/javase/11/tools/java.html

https://blog.csdn.net/u012538947/article/details/104601418

阅读 560 · 发布于 2022-05-25

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
随便看看 换一批