四、本地方法栈
- Java虚拟机栈用于管理Java方法的调用,而本地方法栈用于管理本地方法的调用;
- 在Java中,用native关键字修饰的方法,就是本地方法,其具体的实现不是Java语言编写的,是调用C语言实现的本地方法库;
- 本地方法栈和Java栈一样,当内存不足时,也是会对应的报StackOverFlowError和outofMemorryError异常
五、堆
-
堆是什么
1、一个jvm实例只有一个堆内存,堆也是Java内存管理的核心区域;
2、Java堆区在jvm启动的时候创建,其大小也在这时通过指定的参数确定了;
3、所有的线程共享堆,不过这里也划分了一块缓存区,是线程私有的(Thread Local Allocation Buff,也叫TLAB);
4、在运行时需要用到的对象实例和数组,几乎都在堆上分配内存;
5、在方法结束后,堆中的对象不会给马上删除,仅仅在堆发生GC的时候才会给移除;
6、堆是GC执行垃圾回收的重点区域。 -
堆的内存结构
1、年轻代:
存放生命周期比较短的对象;
-XX:NewRatio = 2,可以设置年轻代和老年代在堆中的内存占比(通常不会修改,年轻代:老年代=1:2);
-Xmn 200M,设置年轻代的总内存是200M;
细分了三个区域;
1)eden区:
刚刚new出来的对象都存放在这里,默认和其他两个s区的占比是8:1:1,可以通过-XX:SurvivorRatio参数修改占比;
2)survivor0区 和 survivor1区:
当eden区满的时候,jvm会发生一次minor GC,这个时候被判断垃圾对象就会给移除,剩下的非垃圾对象,都会转到这两个区域中其中一个空的区域(假设s0区此时是空的,s1区存放了上次GC后的对象,这个时候发生GC,会把Eden区和s1区的非垃圾对象转存到s0区,s1区就变空了。所以这两个区域总有一个是空的)
2、老年代:
存放生命周期比较长的、或者比较大的对象。 当一个对象经历了多次GC后(一般是15次)还没给回收,就会从s0/s1区转存到老年代中,或者这个对象很大,在new的时候也会直接放到老年代;
3、元空间/永久代 -
如何设置堆的大小与OOM
-Xms 500M:堆初始化的内存
-Xmm 500:堆最大的内存
当堆中数据超过-Xmm设置的值时,就会包OOM异常;
一般会把-Xms和-Xmm设置一样大小,这样可以让GC回收完垃圾后可以不用重新分隔计算堆区的大小,提高新能。 -
对象分配过程图解
-
Minor GC、Major GC 和 Full GC
当堆中的某些区域内存满了后,jvm就会触发垃圾回收,也就是GC;在堆中触发的回收有以下几种:
1、Minor GC:发生在年轻代的GC,当Eden区满了触发,执行时会暂停用户进程,执行完后清理年轻代的内存,恢复用户进程;由于Java对象很多都是朝生夕死的,所以这个Minor GC发生比较频繁。
2、Major GC:发生在老年代的GC,当老年代满了触发,执行时也会暂停用户线程,且效率比Minor GC满10倍以上,执行完后清理老年代的内存,恢复用户进程。
3 、Full GC:发生在整个堆的GC,可调用System.gc(),系统执行,但不是必然执行,此外,老年代空间不足、方法区空间不足等情况也会发生,执行时同样也会暂停用户线程且效率较慢,开发中应该尽量避免。 -
关于堆的一些优化操作(基于逃逸分析)
逃逸分析:当一个对象只在当前方法内部创建和使用,则可以认为该对象发生了逃逸分析。
1、栈上分配
当一个对象经过逃逸分析判断发生了逃逸,则将该对象在分配在栈上,随着方法的结束直接移除,就不会占用堆的内存。
2、同步省略
在一个同步代码块中,里面的用到的对象确定了只能从一个线程访问,则可以把同步操作去掉,省去同步代码块的加锁操作。