网站快速排名优化,浙江台州网络设计网站,asp个人网站源码,智慧团建注册志愿者入口首先JVM的内存结构包括五大区域: 程序计数器、虚拟机栈、本地方法栈、方法区、堆区。其中程序计数器、虚拟机栈和本地方法栈3个区域随线程启动与销毁#xff0c; 因此这几个区域的内存分配和回收都具有确定性#xff0c;不需要过多考虑回收的问题。而Java堆区和方法区则不一样…首先JVM的内存结构包括五大区域: 程序计数器、虚拟机栈、本地方法栈、方法区、堆区。其中程序计数器、虚拟机栈和本地方法栈3个区域随线程启动与销毁 因此这几个区域的内存分配和回收都具有确定性不需要过多考虑回收的问题。而Java堆区和方法区则不一样这部分内存的分配和回收是动态的正式垃圾回收需要关注的部分。
垃圾回收在堆内存进行回收前 要先确定区域的哪些对象是可以被回收的、那些对象暂时还不能回收下面谈一谈判断对象是否存活的算法。
判断对象是否存活的算法
1.引用计数算法
引用计数算法堆中的每个对象实例都有一个引用计数器当一个对象被创建时就将该对象实例分配给一个变量该引用计数器设置为1当任何其他变量被赋值为这个对象的引用时计数加1当一个对象实例的某个引用超过了生命周期或被赋为一个新值时 引用计数减1。
任何引用计数器为0的对象实例都可以进行垃圾回收。当一个对象实例被垃圾回收时它引用的所有对象实例引用计数器减1.
优点引用计数器可以很快的执行对程序不需要长时间的打断
缺点无法检测出循环引用。如对象A有对象B的引用对象B又有对象A的引用这样他们的引用计数永远都不为0
2.可达性分析算法
可达性算法将所有的引用关系看作一张图从一个节点GC Root开始寻找对应的引用节点找到后继续寻找这个节点的引用节点当所有引用节点寻找完毕后剩余的节点就被认为是没有被引用的节点即无用节点无用节点被判定为可回收对象。
Java中可以作为GC Root的包括下面几种
虚拟机栈中的引用对象方法区中类静态属性引用的对象方法区中常量引用的对象本地方法栈中引用的对象
对于Java中的引用类型可以看这篇文章Java 控制类的引用类型合理使用内存
常用的垃圾回收算法
1.标记-清除算法
标记-清除算法采用从根集合GC Roots进行扫描对存活的对象进行标记标记完毕后再扫描整个空间中未被标记的对象进行垃圾回收
这种算法实现起来比较容易但是会造成内存碎片
2.标记-复制算法
复制算法是为了解决标记-清除算法的缺陷而提出的。
它将内存划分为大小相等的两块每次只使用其中的一块。当这A快内存用完了就将还存活的对象复制到B块上面然后把A块的内存空间一次性清理掉
这种算法虽然实现简单运行高效且不易产生内存碎片但是却对内存空间的使用做出了高昂的代价因为能使用的空间缩减为原来的一半。很显然复制算法的效率跟存活对象的数量有很大关联若存活对象很多那么效率将大大降低
3.标记-整理算法
该算法是为了解决复制算法的缺陷充分利用内存空间而提出的。
该算法与标记-清除算法一样但是在完成标记后不直接清理可回收对象而是将存活对象全部向一端移动接着清理掉边界以外的内存。
4.分代收集算法
分代收集算法是目前大部分JVM的垃圾收集器采用的算法。其核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。
将其分为年轻代、老年代和永久代。然后根据不同的区域采用合适的收集算法。
Java一般将堆区分为年轻代和老年代将方法区划为永久代。
下面对不同的年龄代进行简单说明
年轻代新创建的对象都存放在这里。因为年轻代会频繁的进行GC清理JVM在年轻代采用的是标记-复制算法先标记出存活的实例然后清除掉无用实例将存活的实例根据年龄每个实例被经历一次GC后年龄会加1拷贝到不同的年龄代。
老年代老年代中是经历了N此垃圾祸首后仍然存活的对象其中的N由JVM的参数决定。这块内存区域一般大于年轻代。GC发生的次数也比年轻代要少。
永久代用于存放静态文件如Java类、方法等。为方法区。
方法区主要回收的内容有废弃的常量、无用的类对与废弃常量可以同过引用的可达性判断但是对于无用类需要同时满足以下3个条件
该类的所有实例都已经被回收了加载该类的 ClassLoader 已经被回收了该类对应的 java.lang.Class 对象没有在任何地方被引用无法在任何地方通过反射访问该类的方法
GC在什么时候触发
GC在优先级最低的线程中运行一般在应用程序空闲时被调用。当内存不足时才会主动调用
因为对象进行了分代处理因此垃圾回收区域、时间也不一样。GC有如下两种
1.Scavenge GC
一般情况下当新对象生成并且在年轻代申请空间失败时会触发Scavenge GC 对年轻代进行垃圾回收。这种方式的GC不会影响到老年代。因为大部分对象都是年轻代开始的同时年轻代内存不会分配的很大所有年轻代的GC会频繁的进行。所以在这里要使用速度快、效率高的算法使其空间尽快空出来。
若GC一次后仍不能满足内存分配JVM会进行二次GC若仍无法满足则报“out of memory的错误Java应用将停止
2.Full GC
对整个内存进行整理包括年轻代、老年代和永久代所以Full GC比Scavenge GC要慢 因此应该尽量减少Full GC的次数。以下可能引发Full GC的原因
老年代被写满永久代被写满System.gc()被显示调用上一次GC后堆的各域分配策略动态变化。Java的垃圾回收介绍到这下面在说说如何在程序中减少GC的开销的几个建议
不要显式调用System.gc()。此函数建议JVM进行GC虽然只是建议但是大多数情况下会触发GC增加了间歇性停顿的次数大大影响系统的性能尽量减少临时对象的使用。也就是减少Scavenge GC执行的机会对象不用时最好显式置为null。将不用的对象置为null有利于GC收集器判定从而提高GC的效率尽量减少静态对象变量。静态变量属于全局变量不会被GC祸首。能有基本类型的就不要用包装类。基本类型变量栈用的内存资源比对应的包装类要少的多使用StringBuffer 而不是String类累加字符串。因为堆String类型进行加的时候会创建新的String对象而StringBuffer是可变长的在原有基础上进行扩增不会产生中间对象分散对象创建或删除的时间。集中在短时间内大量创建新对象特别是大对象会突然需要大量内存JVM在面临这种情况时只能进行GC以回收内存或整合内存碎片从而增加GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主GC的机会。