堆与进程
对堆的认识
一个JVM实例只存在一个堆内存,并且堆内存的大小是可以调节的
如何设置堆内存大小
-Xms10m -Xmx10m
-Xms20m -Xmx20m
使用 JDK 自带的工具:Java VisualVM ,来查看堆内存
代码示例
public class SimpleHeap {
private int id;//属性、成员变量
public SimpleHeap(int id) {
this.id = id;
}
public void show() {
System.out.println("My ID is " + id);
}
public static void main(String[] args) {
SimpleHeap sl = new SimpleHeap(1);
SimpleHeap s2 = new SimpleHeap(2);
int[] arr = new int[10];
Object[] arr1 = new Object[10];
}
}
堆内存细分
Java VisualVM 查看堆内存
设置堆空间大小
代码示例
/**
* 1. 设置堆空间大小的参数
* -Xms 用来设置堆空间(年轻代+老年代)的初始内存大小
* -X 是jvm的运行参数
* ms 是memory start
* -Xmx 用来设置堆空间(年轻代+老年代)的最大内存大小
*
* 2. 默认堆空间的大小
* 初始内存大小:物理电脑内存大小 / 64
* 最大内存大小:物理电脑内存大小 / 4
*
* 3. 手动设置:-Xms600m -Xmx600m
* 开发中建议将初始堆内存和最大的堆内存设置成相同的值。
*
* 4. 查看设置的参数:方式一: jps / jstat -gc 进程id
* 方式二:-XX:+PrintGCDetails
* @author shkstart shkstart@126.com
* @create 2020 20:15
*/
public class HeapSpaceInitial {
public static void main(String[] args) {
//返回Java虚拟机中的堆内存总量
long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
//返回Java虚拟机试图使用的最大堆内存量
long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
System.out.println("-Xms : " + initialMemory + "M");
System.out.println("-Xmx : " + maxMemory + "M");
}
}
OOM举例
/**
* -Xms600m -Xmx600m
* @author shkstart shkstart@126.com
* @create 2020 21:12
*/
public class OOMTest {
public static void main(String[] args) {
ArrayList<Picture> list = new ArrayList<>();
while(true){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add(new Picture(new Random().nextInt(1024 * 1024)));
}
}
}
-Xms600m -Xmx600m
Java 对象的分类
存储在JVM中的Java对象可以被划分为两类:
配置新生代与老年代的比例
配置新生代与老年代在堆结构的占比(一般不会调)
新生区中的比例
代码示例
/**
* -Xms600m -Xmx600m
*
* -XX:NewRatio : 设置新生代与老年代的比例。默认值是2.
* -XX:SurvivorRatio :设置新生代中Eden区与Survivor区的比例。默认值是8
* -XX:-UseAdaptiveSizePolicy :关闭自适应的内存分配策略 (暂时用不到)
* -Xmn:设置新生代的空间的大小。 (一般不设置)
*
* @author shkstart shkstart@126.com
* @create 2020 17:23
*/
public class EdenSurvivorTest {
public static void main(String[] args) {
System.out.println("我只是来打个酱油~");
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
-Xms600m -Xmx600m -XX:SurvivorRatio=8
对象分配过程
对象分配难点:
为新对象分配内存是一件非常严谨和复杂的任务,JVM的设计者们不仅需要考虑内存如何分配、在哪里分配等问题,并且由于内存分配算法与内存回收算法密切相关,所以还需要考虑GC执行完内存回收后是否会在内存空间中产生内存碎片。
对象分配过程
图解对象分配过程
代码示例
/** * -Xms600m -Xmx600m * @author shkstart shkstart@126.com * @create 2020 17:51 */public class HeapInstanceTest { byte[] buffer = new byte[new Random().nextInt(1024 * 200)]; public static void main(String[] args) { ArrayList<HeapInstanceTest> list = new ArrayList<HeapInstanceTest>(); while (true) { list.add(new HeapInstanceTest()); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } }}
思考:幸存区满了咋办?
对象分配的特殊情况
常用的 JVM 调优工具
常用调优工具
Jprofiler 基本使用
总结
Minor GC、Major GC、Full GC
分代收集:
年轻代 GC(Minor GC)触发机制
老年代 GC(MajorGC/Full GC)触发机制
Full GC 触发机制(后面细讲)
触发Full GC执行的情况有如下五种:
说明:Full GC 是开发或调优中尽量要避免的。这样STW时间会短一些
GC 日志分析
/** * 测试MinorGC 、 MajorGC、FullGC * -Xms9m -Xmx9m -XX:+PrintGCDetails * * @author shkstart shkstart@126.com * @create 2020 14:19 */public class GCTest { public static void main(String[] args) { int i = 0; try { List<String> list = new ArrayList<>(); String a = "atguigu.com"; while (true) { list.add(a); a = a + a; i++; } } catch (Throwable t) { t.printStackTrace(); System.out.println("遍历次数为:" + i); } }}
-Xms9m -Xmx9m -XX:+PrintGCDetails
"C:\Program Files\Java\jdk1.8.0_144\bin\java" -Xms9m -Xmx9m -XX:+PrintGCDetails "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.1\lib\idea_rt.jar=13200:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;C:\Users\Heygo\Desktop\JVMDemo\out\production\chapter08" com.atguigu.java1.GCTest[GC (Allocation Failure) [PSYoungGen: 2020K->510K(2560K)] 2020K->812K(9728K), 0.0021339 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 2550K->488K(2560K)] 2852K->2278K(9728K), 0.0005931 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 1949K->504K(2560K)] 3740K->3062K(9728K), 0.0005918 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Ergonomics) [PSYoungGen: 1319K->0K(2560K)] [ParOldGen: 6782K->4864K(7168K)] 8102K->4864K(9728K), [Metaspace: 3452K->3452K(1056768K)], 0.0050464 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 4864K->4864K(9728K), 0.0003452 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 4864K->4846K(7168K)] 4864K->4846K(9728K), [Metaspace: 3452K->3452K(1056768K)], 0.0061555 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 遍历次数为:16Heap PSYoungGen total 2560K, used 134K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000) eden space 2048K, 6% used [0x00000000ffd00000,0x00000000ffd219f0,0x00000000fff00000) from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000) to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000) ParOldGen total 7168K, used 4846K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000) object space 7168K, 67% used [0x00000000ff600000,0x00000000ffabba00,0x00000000ffd00000) Metaspace used 3498K, capacity 4496K, committed 4864K, reserved 1056768K class space used 384K, capacity 388K, committed 512K, reserved 1048576Kjava.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:3332) at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124) at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448) at java.lang.StringBuilder.append(StringBuilder.java:136) at com.atguigu.java1.GCTest.main(GCTest.java:21)Process finished with exit code 0
[Full GC (Ergonomics) [PSYoungGen: 1319K->0K(2560K)] [ParOldGen: 6782K->4864K(7168K)] 8102K->4864K(9728K), [Metaspace: 3452K->3452K(1056768K)], 0.0050464 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
为什么需要分代?
内存分配策略或对象提升(Promotion)规则
针对不同年龄段的对象分配原则如下所示:
代码示例
/** * 测试:大对象直接进入老年代 * -Xms60m -Xmx60m -XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:+PrintGCDetails * * @author shkstart shkstart@126.com * @create 2020 21:48 */public class YoungOldAreaTest { public static void main(String[] args) { byte[] buffer = new byte[1024 * 1024 * 20];//20m }}
-Xms60m -Xmx60m -XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:+PrintGCDetails
"C:\Program Files\Java\jdk1.8.0_144\bin\java" -Xms60m -Xmx60m -XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:+PrintGCDetails "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.1\lib\idea_rt.jar=5495:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;C:\Users\Heygo\Desktop\JVMDemo\out\production\chapter08" com.atguigu.java1.YoungOldAreaTestHeap PSYoungGen total 18432K, used 2637K [0x00000000fec00000, 0x0000000100000000, 0x0000000100000000) eden space 16384K, 16% used [0x00000000fec00000,0x00000000fee935c8,0x00000000ffc00000) from space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000) to space 2048K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffe00000) ParOldGen total 40960K, used 20480K [0x00000000fc400000, 0x00000000fec00000, 0x00000000fec00000) object space 40960K, 50% used [0x00000000fc400000,0x00000000fd800010,0x00000000fec00000) Metaspace used 3469K, capacity 4496K, committed 4864K, reserved 1056768K class space used 381K, capacity 388K, committed 512K, reserved 1048576KProcess finished with exit code 0
问题:堆空间都是共享的么?
不一定,因为还有TLAB这个概念,在堆中划分出一块区域,为每个线程所独占
为什么有TLAB(Thread Local Allocation Buffer)?
什么是 TLAB?
TLAB 的说明
TLAB 分配过程
代码示例
/** * 测试-XX:UseTLAB参数是否开启的情况:默认情况是开启的 * * @author shkstart shkstart@126.com * @create 2020 16:16 */public class TLABArgsTest { public static void main(String[] args) { System.out.println("我只是来打个酱油~"); try { Thread.sleep(1000000); } catch (InterruptedException e) { e.printStackTrace(); } }}
C:\Users\Heygo>jpsC:\Users\Heygo>jinfo -flag UseTLAB 15420
关于空间分配担保
在发生Minor GC之前,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象的总空间。
历史版本
代码示例
/** * 测试堆空间常用的jvm参数: * -XX:+PrintFlagsInitial : 查看所有的参数的默认初始值 * -XX:+PrintFlagsFinal :查看所有的参数的最终值(可能会存在修改,不再是初始值) * 具体查看某个参数的指令: * jps:查看当前运行中的进程 * jinfo -flag SurvivorRatio 进程id * -Xms:初始堆空间内存 (默认为物理内存的1/64) * -Xmx:最大堆空间内存(默认为物理内存的1/4) * -Xmn:设置新生代的大小。(初始值及最大值) * -XX:NewRatio:配置新生代与老年代在堆结构的占比 * -XX:SurvivorRatio:设置新生代中Eden和S0/S1空间的比例 * -XX:MaxTenuringThreshold:设置新生代垃圾的最大年龄 * -XX:+PrintGCDetails:输出详细的GC处理日志 * 打印gc简要信息:① -XX:+PrintGC ② -verbose:gc * -XX:HandlePromotionFailure:是否设置空间分配担保 * * @author shkstart shkstart@126.com * @create 2020 17:18 */public class HeapArgsTest { public static void main(String[] args) { }}
堆是分配对象的唯一选择么?
在《深入理解Java虚拟机》中关于Java堆内存有这样一段描述:
如何将堆上的对象分配到栈,需要使用逃逸分析手段。
逃逸分析举例
public void my_method() { V v = new V(); // use v // .... v = null;}
public static StringBuffer createStringBuffer(String s1, String s2) { StringBuffer sb = new StringBuffer(); sb.append(s1); sb.append(s2); return sb;}
public static String createStringBuffer(String s1, String s2) { StringBuffer sb = new StringBuffer(); sb.append(s1); sb.append(s2); return sb.toString();}
/** * 逃逸分析 * * 如何快速的判断是否发生了逃逸分析,大家就看new的对象实体是否有可能在方法外被调用。 * @author shkstart * @create 2020 下午 4:00 */public class EscapeAnalysis { public EscapeAnalysis obj; /* 方法返回EscapeAnalysis对象,发生逃逸 */ public EscapeAnalysis getInstance(){ return obj == null? new EscapeAnalysis() : obj; } /* 为成员属性赋值,发生逃逸 */ public void setObj(){ this.obj = new EscapeAnalysis(); } //思考:如果当前的obj引用声明为static的?仍然会发生逃逸。 /* 对象的作用域仅在当前方法中有效,没有发生逃逸 */ public void useEscapeAnalysis(){ EscapeAnalysis e = new EscapeAnalysis(); } /* 引用成员变量的值,发生逃逸 */ public void useEscapeAnalysis1(){ EscapeAnalysis e = getInstance(); //getInstance().xxx()同样会发生逃逸 }}
逃逸分析参数设置
逃逸分析结论
开发中能使用局部变量的,就不要使用在方法外定义。
逃逸分析之代码优化
使用逃逸分析,编译器可以对代码做如下优化:
栈上分配
栈上分配举例
/** * 栈上分配测试 * -Xmx256m -Xms256m -XX:-DoEscapeAnalysis -XX:+PrintGCDetails * * @author shkstart shkstart@126.com * @create 2020 10:31 */public class StackAllocation { public static void main(String[] args) { long start = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) { alloc(); } // 查看执行时间 long end = System.currentTimeMillis(); System.out.println("花费的时间为: " + (end - start) + " ms"); // 为了方便查看堆内存中对象个数,线程sleep try { Thread.sleep(1000000); } catch (InterruptedException e1) { e1.printStackTrace(); } } private static void alloc() { User user = new User();//未发生逃逸 } static class User { }}
未开启逃逸分析的情况
-Xmx256m -Xms256m -XX:-DoEscapeAnalysis -XX:+PrintGCDetails
"C:\Program Files\Java\jdk1.8.0_144\bin\java" -Xmx256m -Xms256m -XX:-DoEscapeAnalysis -XX:+PrintGCDetails "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.1\lib\idea_rt.jar=4674:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;C:\Users\Heygo\Desktop\JVMDemo\out\production\chapter08" com.atguigu.java2.StackAllocation[GC (Allocation Failure) [PSYoungGen: 65536K->808K(76288K)] 65536K->816K(251392K), 0.0009467 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 66344K->872K(76288K)] 66352K->880K(251392K), 0.0006768 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 花费的时间为: 46 ms
开启逃逸分析的情况
-Xmx256m -Xms256m -XX:+DoEscapeAnalysis -XX:+PrintGCDetails
"C:\Program Files\Java\jdk1.8.0_144\bin\java" -Xmx256m -Xms256m -XX:+DoEscapeAnalysis -XX:+PrintGCDetails "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.1\lib\idea_rt.jar=4732:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;C:\Users\Heygo\Desktop\JVMDemo\out\production\chapter08" com.atguigu.java2.StackAllocation花费的时间为: 3 ms
同步省略
public void f() { Object hellis = new Object(); synchronized(hellis) { System.out.println(hellis); }}
public void f() { Object hellis = new Object(); System.out.println(hellis);}
字节码分析
public void f() { Object hellis = new Object(); synchronized(hellis) { System.out.println(hellis); }}
分离对象或标量替换
标量替换举例
public static void main(String args[]) { alloc();}class Point { private int x; private int y;}private static void alloc() { Point point = new Point(1,2); System.out.println("point.x" + point.x + ";point.y" + point.y);}
private static void alloc() { int x = 1; int y = 2; System.out.println("point.x = " + x + "; point.y=" + y);}
结论:
标量替换参数设置
参数 -XX:+ElimilnateAllocations:开启了标量替换(默认打开),允许将对象打散分配在栈上。
代码示例
/** * 标量替换测试 * -Xmx100m -Xms100m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-EliminateAllocations * * @author shkstart shkstart@126.com * @create 2020 12:01 */public class ScalarReplace { public static class User { public int id; public String name; } public static void alloc() { User u = new User();//未发生逃逸 u.id = 5; u.name = "www.atguigu.com"; } public static void main(String[] args) { long start = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) { alloc(); } long end = System.currentTimeMillis(); System.out.println("花费的时间为: " + (end - start) + " ms"); }}
未开启标量替换
-Xmx100m -Xms100m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-EliminateAllocations
"C:\Program Files\Java\jdk1.8.0_144\bin\java" -Xmx100m -Xms100m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-EliminateAllocations "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.1\lib\idea_rt.jar=12593:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;C:\Users\Heygo\Desktop\JVMDemo\out\production\chapter08" com.atguigu.java2.ScalarReplace[GC (Allocation Failure) 25600K->816K(98304K), 0.0009418 secs][GC (Allocation Failure) 26416K->792K(98304K), 0.0007337 secs][GC (Allocation Failure) 26392K->792K(98304K), 0.0006104 secs][GC (Allocation Failure) 26392K->856K(98304K), 0.0009474 secs][GC (Allocation Failure) 26456K->824K(98304K), 0.0007392 secs][GC (Allocation Failure) 26424K->808K(101376K), 0.0009449 secs][GC (Allocation Failure) 32552K->720K(101376K), 0.0010633 secs][GC (Allocation Failure) 32464K->720K(100352K), 0.0004493 secs]花费的时间为: 46 msProcess finished with exit code 0
开启标量替换
-Xmx100m -Xms100m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:+EliminateAllocations
"C:\Program Files\Java\jdk1.8.0_144\bin\java" -Xmx100m -Xms100m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:+EliminateAllocations "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.1\lib\idea_rt.jar=12607:C:\Program Files\JetBrains\IntelliJ IDEA 2017.3.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;C:\Users\Heygo\Desktop\JVMDemo\out\production\chapter08" com.atguigu.java2.ScalarReplace花费的时间为: 4 msProcess finished with exit code 0
逃逸分析参数设置总结
-server -Xmx100m -Xms100m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:+EliminateAllocations
这里设置参数如下:
逃逸分析的不足
堆是分配对象的唯一选择么?
综上:对象实例都是分配在堆上。What the fuck?