本帖最后由 小江哥 于 2017-12-6 23:23 编辑
Jvm优化垃圾回收
1、为什么进行垃圾回收一些无效的对象占用内存,为了性能优化,防止内存溢出 2、那么什么是垃圾呢垃圾就是无法到达GC Roots顶点的对象就是垃圾。 3、判断对象是不是垃圾常用算法①、引用计数(Reference Counting)算法
概述:给对象添加一个引用计数器,每有一个地方引用这个对象,计数器值加1,每有一个引用失效则减1。
优点:实现简单、判定效率高
缺点:难以解决对象之间的循环引用问题
作为起点,向下搜索它们引用的对象,可以生成一棵引用树,树的节点视为可达对象,反之视为不可达。 应用实例:Java,C#,Lisp都使用这种算法
JVM使用“可达性分析算法”来判定一个对象是否会可以被回收,有两个细节需要注意:
Java 中可作为GC Roots的对象包括以下几种:
a. 虚拟机栈(帧栈中的本地变量表)中引用的对象
b. 方法区中静态属性引用的对象
c. 方法区中常量引用的对象
d. 本地方法栈中JNI引用的对象2.不可达对象一定会被回收吗 ?
执行垃圾回收前JVM会执行不可达对象的finalize方法,如 果执行完毕之后该对象变为可达,则不会被回收它。
但一个对象的finalize方法只会被执行一次
4、引用的类型说了这么多,其实我们可以看到,所有的垃圾回收机制都是和引用相关的,那我们来具体的来看一下引用的分类,到底有哪些类型的引用?每种引用都是做什么的呢? ·Java中存在四种引用,每种引用如下 ①、强引用只要引用存在,垃圾回收器永远不会回收 Object obj = new Object(); 可直接通过obj取得对应的对象 如obj.equels(new Object()); 而这样 obj对象相对后面new Object的一个强引用,只有当obj这个引用被释放之后,对象才会被释放掉,这也是我们经常所用到的编码形式。 ②、 软引用非必须引用,内存溢出之前进行回收,可以通过以下代码实现 Object obj = new Object(); SoftReference<Object> sf = new SoftReference<Object>(obj); obj = null; sf.get();//有时候会返回null 这时候sf是对obj的一个软引用,通过sf.get()方法可以取到这个对象,当然,当这个对象被标记为需要回收的对象时,则返回null;
软引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据。 ③、 弱引用第二次垃圾回收时回收,可以通过如下代码实现 Object obj = new Object(); WeakReference<Object> wf = new WeakReference<Object>(obj); obj = null; wf.get();//有时候会返回null wf.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾 弱引用是在第二次垃圾回收时回收,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,将返回null。 弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued方法返回对象是否被垃圾回收器回收 ④、 虚引用(幽灵/幻影引用) 垃圾回收时回收,无法通过引用取到对象值,可以通过如下代码实现 Object obj = new Object(); PhantomReference<Object> pf = new PhantomReference<Object>(obj); obj=null; pf.get();//永远返回null pf.isEnQueued();//返回从内存中已经删除 虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,因此也被成为幽灵引用。 虚引用主要用于检测对象是否已经从内存中删除。
5、虚拟机的结构虚拟机中的共划分为三个代:年轻代(Young Generation)、年老代(Old Generation)和持久代(Permanent Generation)。 其中持久代主要存放的是Java类的类信息,与垃圾收集要收集的Java对象关系不大。 年轻代和年老代的划分是对垃圾收集影响比较大的。 ①、年轻代:所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个Survivor区(一般而言)。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来 对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。 ②、年老带:在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。 ③、持久代:用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=<N>进行设置。 6、垃圾回收器的分类和什么时候被调用GC有两种类型:Scavenge GC和Full GC。 Scavenge GC 一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。 Full GC 对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个对进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC: · 年老代(Tenured)被写满 · 持久代(Perm)被写满 · System.gc()被显示调用 ·上一次GC之后Heap的各域分配策略动态变化 7、垃圾收集算法
①、标记清除算法标记-清除算法采用从根集合进行扫描,对存活的对象对象标记,标记完毕后,再扫描整个空间中未被标记的对象,进行回收,如上图所示。 标记-清除算法不需要进行对象的移动,并且仅对不存活的对象进行处理,在存活对象比较多的情况下极为高效,但由于标记-清除算法直接回收不存活的对象,因此会造成内存碎片! ②、复制算法 复制算法采用从根集合扫描,并将存活对象复制到一块新的,没有使用过的空间中,这种算法当控件存活的对象比较少时,极为高效,但是带来的成本是需要一块内存交换空间用于进行对象的移动。 ③、标记-整理算法 标记-整理算法采用标记-清除算法一样的方式进行对象的标记,但在清除时不同,在回收不存活的对象占用的空间后,会将所有的存活对象往左端空闲空间移动,并更新对应的指针。标记-整理算法是在标记-清除算法的基础上,又进行了对象的移动,因此成本更高,但是却解决了内存碎片的问题
众览群雄,唯我杭城独秀—一贴汇总杭州校区所有就业薪资
|