首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Java 7进行转义分析/堆栈分配的资格

使用Java 7进行转义分析/堆栈分配的资格
EN

Stack Overflow用户
提问于 2012-01-27 11:18:14
回答 3查看 4.4K关注 0票数 12

我正在使用Java7中的逃逸分析进行一些测试,以便更好地了解哪些对象有资格进行堆栈分配。

下面是我为测试堆栈分配而编写的代码:

代码语言:javascript
复制
import java.util.ArrayList;
import java.util.Iterator;


public class EscapeAnalysis {

    private static final long TIME_TO_TEST = 10L * 1000L; // 10s

    static class Timestamp {
        private long millis;
        public Timestamp(long millis) {
            this.millis = millis;
        }
        public long getTime() {
            return millis;
        }
        public void setTime(long time) {
            millis = time;
        }
    }

    public static void main(String[] args) {
        long r = 0;
        System.out.println("test1");
        r += test1();
        System.out.println("test2");
        r += test2();
        System.out.println("test3");
        r += test3();
        System.out.println("test4");
        r += test4();
        System.out.println("test5");
        r += test5();
        System.out.println("test6");
        r += test6();
        System.out.println(r);
    }

    public static long test1() {
        long r = 0;
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < TIME_TO_TEST) {
            r += new Timestamp(System.currentTimeMillis()).getTime();
        }
        return r;
    }

    public static long test2() {
        ArrayList<Integer> l = new ArrayList<Integer>(1000);
        for (int i = 0; i < 1000; ++i) {
            l.add(i);
        }
        long r = 0;
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < TIME_TO_TEST) {
            for (Iterator<Integer> it = l.iterator(); it.hasNext(); ) {
                r += it.next().longValue();
            }
        }
        return r;
    }

    public static long test3() {
        long r = 0;
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < TIME_TO_TEST) {
            Timestamp ts = new Timestamp(System.currentTimeMillis());
            ts.setTime(42);
            r += ts.getTime();
        }
        return r;
    }

    public static long test4() {
        ArrayList<Integer> l = new ArrayList<Integer>(1000);
        for (int i = 0; i < 1000; ++i) {
            l.add(i);
        }
        long r = 0;
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < TIME_TO_TEST) {
            Iterator<Integer> it = l.iterator();
            r += it.next().longValue();
            r += it.next().longValue();
            r += it.next().longValue();
            r += it.next().longValue();
        }
        return r;
    }

    public static long test5() {
        ArrayList<Integer> l = new ArrayList<Integer>(1000);
        for (int i = 0; i < 1000; ++i) {
            l.add(i);
        }
        long r = 0;
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < TIME_TO_TEST) {
            Iterator<Integer> it = l.iterator();
            for (int i = 0; i < l.size(); ++i) {
                r += it.next().longValue();
            }
        }
        return r;
    }

    public static long test6() {
        long r = 0;
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < TIME_TO_TEST) {
            for (Timestamp ts = new Timestamp(System.currentTimeMillis());
                    ts.getTime() > 0;
                    ts.setTime(ts.getTime() + System.currentTimeMillis())) {
                r += ts.getTime();
            }
        }
        return r;
    }

}

下面是它在Linux上使用Java 7输出的内容

代码语言:javascript
复制
java -server -version                                                 
java version "1.7.0_02"
Java(TM) SE Runtime Environment (build 1.7.0_02-b13)
Java HotSpot(TM) 64-Bit Server VM (build 22.0-b10, mixed mode)

java -server -verbose:gc -XX:CompileThreshold=1 -cp bin EscapeAnalysis
test1
test2
[GC 15616K->352K(59776K), 0,0014270 secs]
[GC 15968K->288K(59776K), 0,0011790 secs]
[GC 15904K->288K(59776K), 0,0018170 secs]
[GC 15904K->288K(59776K), 0,0011100 secs]
[GC 15904K->288K(57152K), 0,0019790 secs]
[GC 15520K->320K(56896K), 0,0011670 secs]
[GC 15232K->284K(56256K), 0,0011440 secs]
test3
test4
test5
[GC 14876K->348K(55936K), 0,0005340 secs]
[GC 14620K->348K(56000K), 0,0004560 secs]
[GC 14300K->316K(55296K), 0,0004680 secs]
[GC 13948K->316K(55488K), 0,0003590 secs]
[GC 13692K->316K(54784K), 0,0004580 secs]
[GC 13436K->316K(54976K), 0,0005430 secs]
[GC 13180K->316K(54272K), 0,0004500 secs]
[GC 12924K->316K(54464K), 0,0005090 secs]
[GC 12668K->316K(53760K), 0,0004490 secs]
[GC 12412K->316K(53888K), 0,0004350 secs]
[GC 12156K->316K(53312K), 0,0005060 secs]
test6
6737499643744733086

我正在使用GC日志来知道是否在堆栈上分配了对象(来自Java中的逸出分析的想法),这可能不是100%可靠的,但似乎给出了很好的提示。

在输出的基础上,堆栈分配工作于test1、test3、test4和test6,而不适用于test2和test5。我不明白为什么在for-循环中迭代器不能工作,尽管它可以工作。

  • 在for-循环外使用迭代器(请参阅test4),
  • 在for-循环中使用另一个对象(请参见test6)。

我已经阅读了ArrayList迭代器的代码,我不明白为什么它不符合测试2和5中堆栈分配的条件,因为它既不能逃脱当前的方法,也不能逃避当前的线程。

有什么想法吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-01-27 15:58:39

EA是C2编译器基于它生成的IR来分析的东西,因此您需要它来编译这个方法,然后才能享受它的好处。每个测试只调用一次,因此没有机会进行编译。热点内部wiki中EA和C2 IR的详细信息(https://wiki.openjdk.java.net/display/HotSpot/Overview+of+Ideal,+C2%27s+high+level+intermediate+representationhttps://wiki.openjdk.java.net/display/HotSpot/EscapeAnalysis)

下面是一个试图展示影响的版本

代码语言:javascript
复制
import com.sun.management.ThreadMXBean;

import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Iterator;


public class EscapeAnalysisTest {

    private static final long TIME_TO_TEST = 10L * 1000L; // 10s

    static class Timestamp {
        private long millis;

        public Timestamp(long millis) {
            this.millis = millis;
        }

        public long getTime() {
            return millis;
        }

        public void setTime(long time) {
            millis = time;
        }
    }

    public static void main(String[] args) {
        System.out.println("****");
        doIt();
        System.out.println("****");
        doIt();
        System.out.println("****");
        doIt();
        System.out.println("****");
        doIt();
        System.out.println("****");
    }

    private static void doIt() {
        final ThreadMXBean mxbean = (ThreadMXBean) ManagementFactory.getThreadMXBean();
        final long tid = Thread.currentThread().getId();
        long r = 0;
        final long allocPre = mxbean.getThreadAllocatedBytes(tid);
        r += test1();
        long alloc1 = mxbean.getThreadAllocatedBytes(tid);
        System.out.println("test1 - " + (alloc1 - allocPre));
        r += test2();
        final long alloc2 = mxbean.getThreadAllocatedBytes(tid);
        System.out.println("test2 - " + (alloc2 - alloc1));
        r += test3();
        final long alloc3 = mxbean.getThreadAllocatedBytes(tid);
        System.out.println("test3 - " + (alloc3 - alloc2));
        r += test4();
        final long alloc4 = mxbean.getThreadAllocatedBytes(tid);
        System.out.println("test4 - " + (alloc4 - alloc3));
        r += test5();
        final long alloc5 = mxbean.getThreadAllocatedBytes(tid);
        System.out.println("test5 - " + (alloc5 - alloc4));
        r += test6();
        final long alloc6 = mxbean.getThreadAllocatedBytes(tid);
        System.out.println("test6 - " + (alloc6 - alloc5));
        System.out.println(r);
    }

    public static long test1() {
        long r = 0;
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < TIME_TO_TEST) {
            r += new Timestamp(System.currentTimeMillis()).getTime();
        }
        return r;
    }

    public static long test2() {
        ArrayList<Integer> l = new ArrayList<Integer>(1000);
        for (int i = 0; i < 1000; ++i) {
            l.add(i);
        }
        long r = 0;
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < TIME_TO_TEST) {
            for (Iterator<Integer> it = l.iterator(); it.hasNext(); ) {
                r += it.next().longValue();
            }
        }
        return r;
    }

    public static long test3() {
        long r = 0;
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < TIME_TO_TEST) {
            Timestamp ts = new Timestamp(System.currentTimeMillis());
            ts.setTime(42);
            r += ts.getTime();
        }
        return r;
    }

    public static long test4() {
        ArrayList<Integer> l = new ArrayList<Integer>(1000);
        for (int i = 0; i < 1000; ++i) {
            l.add(i);
        }
        long r = 0;
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < TIME_TO_TEST) {
            Iterator<Integer> it = l.iterator();
            r += it.next().longValue();
            r += it.next().longValue();
            r += it.next().longValue();
            r += it.next().longValue();
        }
        return r;
    }

    public static long test5() {
        ArrayList<Integer> l = new ArrayList<Integer>(1000);
        for (int i = 0; i < 1000; ++i) {
            l.add(i);
        }
        long r = 0;
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < TIME_TO_TEST) {
            Iterator<Integer> it = l.iterator();
            for (int i = 0; i < l.size(); ++i) {
                r += it.next().longValue();
            }
        }
        return r;
    }

    public static long test6() {
        long r = 0;
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < TIME_TO_TEST) {
            for (Timestamp ts = new Timestamp(System.currentTi());
                 ts.getTime() > 0;
                 ts.setTime(ts.getTime() + System.currentTimeMillis())) {
                r += ts.getTime();
            }
        }
        return r;
    }

}

在使用-server -XX:CompileThreshold=1运行时生成以下输出

代码语言:javascript
复制
****
test1 - 109048
test2 - 89243416
test3 - 16664
test4 - 42840
test5 - 71982168
test6 - 1400
-5351026995119026839
****
test1 - 16432
test2 - 85921464
test3 - 16664
test4 - 42840
test5 - 66777600
test6 - 1368
7844020592566674506
****
test1 - 48
test2 - 18256
test3 - 272
test4 - 18264
test5 - 18264
test6 - 272
-2137858376905291730
****
test1 - 48
test2 - 18256
test3 - 272
test4 - 18264
test5 - 18264
test6 - 272
3273987624143297143
****

这里的一个危险是,这个方法的编译已经从根本上改变了它,我没有试图防止这种情况,所以可能需要使用LogCompilationPrintCompilation进行检查。

票数 8
EN

Stack Overflow用户

发布于 2012-05-08 02:37:32

转义分析在很大程度上依赖于函数调用的内联。

与任何其他微基准测试一样--特别是在服务器上--热身是必需的。如果删除-XX:CompileThreshold=1并在循环中执行主测试,您将注意到,在1-2次迭代之后,它将停止收集垃圾,因为编译器收集了足够的分析信息,可以内联这些方法,然后执行转义分析。

票数 2
EN

Stack Overflow用户

发布于 2017-03-24 21:44:59

我只是研究了同样的事情,但是对于Java 8,我把答案放在一个重复的问题中,因为我没有及时找到这个问题。

来自完整的答案的摘要

首先,它依赖于实现。这个答案适用于OracleJVM1.8,可能也适用于OracleJVM1.8。

其次,正如其他人所指出的,堆栈分配只在C2编译器编译一个方法时发生,只有在方法被调用足够多次之后才会发生。

如果是,则可以在以下情况下分配对象:

  • 所有使用它的方法调用都是内联的。
  • 它从不分配给任何静态或对象字段,只分配给局部变量(内联方法调用的参数成为局部变量)。
  • 在程序的每一点,包含对对象的引用的局部变量必须是JIT时间可确定的,并且不依赖于任何不可预测的条件控制流。
  • 如果对象是一个数组,那么它的大小必须是JIT时间常数,索引到它的时候必须使用JIT时间常量。

如果你不知道Hotspot的一些具体的怪癖,尤其是内衬是不可预测的。有关详细信息,请参阅链接答案。

编辑:我试着在java 8 (OpenJDK)上运行您的测试,所有内容都是内联的。因此,java 7和8在堆栈分配方面存在差异。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9032519

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档