目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

内存分配

Java 堆内存分配

Java 堆分布图

对象分配遵循的规则

  • 对象主要分配在新生代的 Eden 区上。
  • 如果启动了本地线程分配缓冲,将按线程优先在 TLAB 上分配。
  • 少数情况下也可能会直接分配在老年代中。(新生代放不下的情况下、对象比较大的情况下[几乎占据 From or To 区的全部,不如直接复制到老年代]。)

查看对象分配在哪个区域

1//-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC
2
3public class TestJVMFirst {
4    private static final int byteSize = 1024 * 1024;
5    public static void main(String[] args) {
6        byte[] bytes = new byte[40 * byteSize]; //40MB内存
7    }
8}

控制台输出

1Heap
2 PSYoungGen      total 76288K, used 47514K [0x000000076b000000, 0x0000000770500000, 0x00000007c0000000)
3  eden space 65536K, 72% used [0x000000076b000000,0x000000076de66878,0x000000076f000000)
4  from space 10752K, 0% used [0x000000076fa80000,0x000000076fa80000,0x0000000770500000)
5  to   space 10752K, 0% used [0x000000076f000000,0x000000076f000000,0x000000076fa80000)
6 ParOldGen       total 175104K, used 0K [0x00000006c1000000, 0x00000006cbb00000, 0x000000076b000000)
7  object space 175104K, 0% used [0x00000006c1000000,0x00000006c1000000,0x00000006cbb00000)
8 Metaspace       used 3509K, capacity 4498K, committed 4864K, reserved 1056768K
9  class space    used 387K, capacity 390K, committed 512K, reserved 1048576K

新生代与老年代的垃圾收集

  • 新生代 GC (Minor GC):指发生在新生代的垃圾收集动作,因为 Java 对象大多都具备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度老年代
  • GC (Major GC/ Full GC):指发生在老年代的 GC,出现了 Major GC,经常会伴随至少一次的 Minor GC(但非绝对的,在 Parallel Scavenge 收集器的收集策略里就有直接进行 Major GC 的策略选择过程)。Major GC 的速度一般会比 Minor GC 慢 10 倍以上。

大对象的分配

 所谓的大对象是指,需要大量连续内存空间的 Java 对象,最典型的大对象就是那种很长的字符串以及数组。
 虚拟机提供了一个-XX: PretenureSizeThreshold 参数,令大于这个设置值的对象直接在老年代分配。这样做的目的是避免在 Eden 区及两个 Survivor 区之间发生大量的内存复制

配置参数含义

1-verbose:gc -XX:+PrintGCDetails 开启GC日志打印
2-Xms20 M 设置JVM初始内存为20M
3-Xmx20 M 设置JVM最大内存为20M
4-Xmn10 M 设置年轻代内存大小为10M

TestMaxObject

1//-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:PretenureSizeThreshold=3145728    
2// 3MB
3public class TestMaxObject {
4    public static void main(String[] args) {
5        byte[] bytes = new byte[1024*1024*10]; //10MB
6    }
7}

控制台输出

1Heap
2 def new generation   total 78656K, used 6995K [0x00000006c1000000, 0x00000006c6550000, 0x0000000716000000)
3  eden space 69952K,  10% used [0x00000006c1000000, 0x00000006c16d4f68, 0x00000006c5450000)
4  from space 8704K,   0% used [0x00000006c5450000, 0x00000006c5450000, 0x00000006c5cd0000)
5  to   space 8704K,   0% used [0x00000006c5cd0000, 0x00000006c5cd0000, 0x00000006c6550000)
6 tenured generation   total 174784K, used 10240K [0x0000000716000000, 0x0000000720ab0000, 0x00000007c0000000)
7   the space 174784K,   5% used [0x0000000716000000, 0x0000000716a00010, 0x0000000716a00200, 0x0000000720ab0000)
8 Metaspace       used 3509K, capacity 4498K, committed 4864K, reserved 1056768K
9  class space    used 387K, capacity 390K, committed 512K, reserved 1048576K

逃逸分析

 逃逸分析的基本行为就是分析对象动态作用域。

  • 当一个对象在方法中被定义后,它可能被外部方法所引用,称为方法逃逸
 1public class TestEscape {
 2    public static Object obj;
 3
 4    public void variableEscape(){
 5        obj = new Object(); //方法逃逸
 6    }
 7    
 8    public Object methodEscape(){
 9        return new Object(); //方法逃逸
10    }
11}
  • 甚至还有可能被外部线程访问到,譬如赋值给类变量或可以在其他线程中访问的实例变量,称为线程逃逸

栈上分配

 栈上分配就是把方法中的变量和对象分配到栈上,方法执行完后自动销毁,而不需要垃圾回收的介入,从而提高系统性能。

  • 开启逃逸分析(JDK1.8 默认开启):就进行栈上分配。
  • 关闭逃逸分析:堆分配。

参数含义

1-XX:+DoEscapeAnalysis开启逃逸分析(jdk1.8默认开启,其它版本未测试)
2-XX:-DoEscapeAnalysis 关闭逃逸分析
3
4// 当关闭逃逸分析:频繁日志输出分配内存失败
5-verbose:gc -XX:+PrintGCDetails -XX:-DoEscapeAnalysis 关闭逃逸分析

TestEscape

 1public class TestEscape {
 2
 3    public static Object obj;
 4
 5    public void variableEscape(){
 6        obj = new Object(); //方法逃逸
 7    }
 8
 9    public Object methodEscape(){
10        return new Object(); //方法逃逸
11    }
12
13    public static void alloc(){
14        byte[] b = new byte[2];
15        b[0] = 1;
16    }
17
18    public static void main(String[] args) {
19        long start = System.currentTimeMillis();
20        for(int i =0; i < 100000000; i++){
21            alloc();
22        }
23        long end = System.currentTimeMillis();
24        System.out.println(end-start);
25    }
26}

控制台输出

1-XX:-DoEscapeAnalysis (关闭逃逸分析,堆在分配)
2	912 毫秒
3-XX:+DoEscapeAnalysis(开启逃逸分析,栈上分配)
4	5 毫秒

作者:Soulboy