GC
为减轻GC压力,我们需要注意些什么?
软件如何设计架构
代码如何写
堆空间如何分配(不要把对内存-xmx,-xms调的太大,gc时间长)
像小对象不要太多啊,容易经常产生碎片要碎片整理(比如可以对象池啊啥的重用)
大对象不要太大啊,gc时间太长(分页、分块啥的)
gc的时候会查看对象是否被jvm调用过finalize(),如果把这个对象丢到一个finalizer的队列里,由finalizer终结器线程处理对象内存释放前的一些自定义的结束操作
把它加入到队列的时候,这时会标记已经被加入到finalizer队列里了,以后就不会加入了,就算被复活了,但是自己调用finalize(),不会标记这个,不影响,所以gc的时候只会执行一次finalize()方法
gc和finalizer不同线程,另外gc可以并行收集,不同线程因为怕无限等待finalize(),jvm崩掉
然而这个线程的优先级较低,可能第二次gc都开始了,所以并不保证finalizer会调用在前,或者被调用
所以像socket,conn,reader,file这种非内存资源东西的关闭,最好不止写在重载的finalize()方法里,可以try{}catch{}finally{},在finally里面关闭掉
在gc的时候发现对象已经标记过加入finalizer队列的就会直接释放内存完成清理了
在finalize里可能会复活对象,可能重新附上引用,那么就又是可达的了,就不会被gc清理了,但是因为gc只会调用一次finalize,所以,不手动是不会复活两次的
Java中可以作为GC ROOT的对象有:
静态变量引用的对象
常量引用的对象
本地方法栈(JNI)引用的对象
Java栈中引用的对象
可达可触及:
可触及的–从GC ROOT这个根节点对象,沿着引用的链条,可以触及到这个对象,该对象就叫可触及的,也就是之前说的可达性算法的思想。
可复活的–一旦所有引用被释放,就是可复活状态,因为在finalize()中可能复活该对象(finalize方法只会调用一次)。
不可触及的–在finalize()后,可能会进入不可触及状态,不可触及的对象不可能复活,就可以回收了。
finalize()在什么时候被调用?
有三种情况:
所有对象被Garbage Collection时自动调用,比如运行System.gc()的时候。
程序退出时为每个对象调用一次finalize方法。
显式的调用finalize方法。
java没用引用计数法,要加减引用消耗不小,或者怕出现循环引用,没用的释放不掉
jvm的垃圾回收器有很多种,比如并行收集器,串行收集器,与用户线程一起执行的停顿短暂的并行收集器
其中有可能
java采用的是标记清楚对新生代或者复制算法(比较快,费一般内存),标记压缩清楚对老年代(节省空间)
gc的时候会stop-the-world,因为会怕新的对象产生,引用变化或新垃圾产生,还有压缩或者碎片整理可能会移动内存,会停在暂停点,没在的可能会被线程劫持,停在暂停点
其它的stop-the-world可能是由于程序员获取snapshot,dump,或者jvm死锁检测引起的
这是Java中一种全局暂停的现象,全局停顿,所有Java代码停止,类似JVM挂起的状态……但是native代码可以执行,但不能和JVM交互
强引用会让gc-root对象可达
弱引用,如果没有强引用了,立刻可以被gc回收
软饮用,如果没有强引用,会在没内存的时候jvm调用gc回收,可以用来做缓存
虚引用,没什么实际用,标记可以被回收了