1 JVM内存分区
内存分区模型图
1.1 JVM栈
JVM栈是线程私有的,每个线程创建的同时都会创建JVM栈,栈中存的是基本数据类型和堆中对象的引用(java中定义的八种基本类 型:boolean、char、byte、short、int、long、float、double),由于JVM栈是线程私有的,因此其在内存分配上非常高效,并且当线程运行完毕后,这些内存也就被自动回收。
1.2 Heap(java堆)
是大家最为熟悉的区域,它是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中 的对象的内存需要等待GC进行回收,Heap在32位的操作系统上最大为2G,在64位的操作系统上则没有限制,其大小通过-Xms和-Xmx来控制
1.3 方法区(Method Area)
用于存储类结构信息的地方,包括常量池、静态变量、构造函数等。虽然JVM规范把方法区描述为堆的一个逻辑部分, 但它却有个别名non-heap(非堆),所以大家不要搞混淆了。方法区还包含一个运行时常量池。
1.4 程序计数器(PC Register)
用于保存当前线程执行的内存地址。由于JVM程序是多线程执行的(线程轮流切换),所以为了保证线程切换回来后,还能恢复到原先状态,就需要一个独立的计数器,记录之前中断的地方,可见程序计数器也是线程私有的。
1.5 本地方法栈(Native Method Stack)
和java栈的作用差不多,只不过是为JVM使用到的native方法服务的。(本地方法栈)
补充:
本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。
2 JVM内存分配
eden+from+to:1/3(from和to各占十分之一)
old:2/3
将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中的一块Survivor。当回收时,将Eden和Survivor中还存活着的对象一次性地拷贝到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,也就是每次新生代中可用内存空间为整个新生代容量的90%(80%+10%),只有10%的内存是会被“浪费”的。当然,并不能保证每次回收都只有10%的对象存活,当Survivor空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保(Handle Promotion)。即如果另外一块Survivor空间没有足够的空间存放上一次新生代收集下来的存活对象,这些对象将直接通过分配担保机制进入老年代。
自动内存管理主要解决了两个问题:给对象分配内存和回收分配给对象的内存。
2.1、对象优先在Eden分配
Eden区内存不够时虚拟机将发起一次Minor GC。
收集器日志参数: -XX:+PrintGCDetails 告诉虚拟机在发生垃圾收集行为时打印内存回收日志,并在进程退出时输出当前内存各区域的分配情况,实际中内存回收日志一般是打印到文件后通过日志工具进行分析。
2.2、大对象直接进入老年代
大对象值需要大量连续空间的Java对象,最典型的大对象就是很长的字符串及数组。最坏的情况是朝生夕灭的大对象。经常出现大对象容易导致内存还有不少空间时就提前触发垃圾收集以获取足够的连续空间来存放大对象。
-XX:PretenureSizeThreshold 大于这个设置值的对象直接在老年代中分配,该设置的优点是避免在Eden区及两个Survivor区之间发生大量的内存拷贝(因为新生代采用复制算法收集内存)。但该参数只对Serial/ParNew两款收集器有效。
2.3、长期存活的对象将进入老年代
虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在Eden出生并在经历了一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中去,并将对象年龄设为1。对象在Survivor区中每熬过一次Minor GC,年龄就增加一岁,当它的年龄增加到一定程度(默认为15岁)时,就会被晋升到老年代中。
-XX:MaxTenuringThreshold 对象晋升老年代的年龄阈值
2.4、动态对象年龄判定
如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代。无须等到MaxTenuringThreshold中要求的年龄。
2.5、空间分配担保
在发生Minor GC时,虚拟机会检测之前每次晋升到老年代的平均大小是否大于老年代的剩余空间大小,
如果大于,则改为直接进行一次Full GC。
如果小于,则查看HandlePromotionFailure设置是否允许担保失败;如果允许,那只会进行Minor GC;
如果不允许,则也要改为进行一次Full GC。