jvm内存模型可以分为 堆、方法区、虚拟机栈、本地方法栈、程序计数器五个区域。见下图。
1.方法区
方法区是各个线程共享的内存区域,用于存储类的信息、常量与静态变量。
HotSpot VM把方法区也称为永久代,Permanent Space。永久代对垃圾回收没有显著影响。
1.1 vm参数
-XX:PermSize //永久代最小空间
-XX:MaxPermSize //永久代最大空间
示例: -XX:PermSize=100m -XX:MaxPermSize
1.2相关报错
java.lang.OutOfMemoryError:Perm space
永久代内存溢出,调大内存即可。
2.堆
堆通常是jvm内存中最大的一块,用于盛放对象。
堆又可以分为新生代与老年代,见图2-1.
图2-1 jvm堆与方法区的内存模型
permanent space为永久代空间;heap space为堆空间,它又分为新生代空间与老年代空间。
2.1新生代空间
它又可以分为2部分——Eden space与Survivor Spaces,后者包括s0与s1。
2.1.1 Eden space
Eden ['i:dən]
n. 伊甸园(《圣经》中亚当和夏娃最初居住的地方)
新创建的对象将放入Eden Space。
2.1.2 survivor space
又可以分为 s0 (survivor 0) 与 s1(survivor 1)。
当Eden区满时,触发一次minor GC。还存活的对象将被复制到s0,同时清空Eden区域。
当Eden区再次满时,触发一次minor GC。Eden+s0(不管s0有没有满)还存活的对象将被复制到s1,同时清空Eden+s0区域。
当Eden区再次满时,触发一次minor GC。Eden+s1(不管s1有没有满)还存活的对象将被复制到s0,同时清空Eden+s1区域。
如此往复。。。
注意survivor from 与survivor to的概念和s0与s1的概念。
2.1.3 复制算法
新生代采用的就是复制算法。当survivor from 内存用完,就将还活着的对象复制到另外一块survivor to上面。复制算法不会产生内存碎片。
新生代GC也是需要stop the world的,通常为几十毫秒。
2.2 老年代空间
在新生代中经历了N次垃圾回收后仍然存活的对象,就会被放到老年代中。因此可以认为老年代中存放的都是一些生命周期较长的对象。
2.3 年龄
一个对象,每经历一次GC(minor GC 或 major GC),年龄就加一岁。
-XX:MaxTenuringThreshold=15
指定对象到达15岁时被移到Old区。默认值为15。
需要注意的是,并不是年龄非得到达指定值后才会被移到老年代,JVM还有自己的一套规则,烦人。
-XX:+PrintTenuringDistribution
这个参数用于显示每次Minor GC时Survivor区中各个年龄段的对象的大小。
2.4 vm参数
-Xmx
//最大堆内存
-Xms
//最小堆内存
-Xmn
//新生代内存
示例:-Xms2048m -Xmx5120m
-XX:SurvivorRatio//新生代中,Eden区域与survivor区域的比值。默认为8。
2.5 常见错误
java.lang.OutOfMemoryError:java heap space
堆溢出。
3. 程序计数器
名为PC,Program Counter,是一个寄存器,存放该线程要执行的下一条语句的位置。
每个线程都有自己的PC,线程间不共享。
4.java虚拟机栈
每个线程都有自己的java虚拟机栈,线程间不共享。
每当进入一个函数时,创建栈帧(即Stack Frame,内含局部变量与函数出口等信息),入栈,函数执行完退出时,出栈。
4.1vm参数
-Xss
//每个线程的栈大小,默认为1M
示例: -Xss 2m
4.2 常见报错
java.lang.StackOverflowError
栈溢出。默认情况下函数嵌套达到2000层是没有问题的。可以认为调大栈空间。
5.本地方法栈
每个线程都有自己的本地方法栈,线程间不共享。
与java虚拟机栈类似,服务于本地(native)代码。