JVM垃圾回收算法以及引用类型
一.垃圾回收算法
垃圾回收是java体系最重要的组成部分之一.垃圾回收顾名思义就是把不用的对象给丢弃,释放内存空间.
接下来来看看java垃圾回收机制的理论基础:引用技术法,标记压缩法,标记清楚法,复制算法和分代分区.
1.引用技术法
引用计算法的实现很简单,对于一个对象a,只要有任何一个根对象引用它,那么计数加一,引用失效时,计数减1.为0时,就不能再被引用了.
根对象主要有:
1.栈中引用的对象; 2.类静态属性引用的对象. 3.JN引用的对象
这种方法有两个很严重的问题:
- 无法处理循环引用.
- 每次计算都要加减,影响性能。
所以垃圾回收器不使用这种算法.
2.标记清除法
标记清除法把垃圾分为两个阶段:标记阶段和清除阶段.
在标记阶段,从根结点触发,标记所有可达的对象。因此,未被标记的对象就是垃圾对象.这个实现的最大问题就是空间碎片.
3.复制算法
把内存空间分为两份,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的空间中,之后清除正在使用的内存块.交换两者角色,完成回收.
这种算法的缺点就是浪费空间.
java的新生代串行垃圾回收器中,使用了这种思想 .分成eden,from,to三个部分.
From和to也被称为survivor空间,即幸存者空间.垃圾回收时,eden中存活对象会被复制到to空间,from中正在使用的对象也会被存放到to.to空间满了也会被放到老年代.这个时候eden,from里面的空间就是垃圾对象.
复制算法适合新生代,因为垃圾对象多于存活对象.
4.标记压缩法
复制算法高效是因为,存活对象少,但是在老年代存活对象多,如果还是使用复制算法,那么复制成本很高.
标记压缩法是一种老年代的回收算法.它在标记清除算法的基础上做了一些优化.区别在于标记之后,先将所有存活对象压缩到内存的一端。之后清除边界外所有空间.
5. 分代算法
分代算法,将内存空间根据对象的特点分为几块,根据每块区域的特点使用不同的回收算法。
对于新生代来说,回收的频率很高,但是每次回收的耗时很短,老年代回收频率低,耗时长.为了支持高频率的新生代回收,虚拟机可能使用了一种叫做卡表的数据结构.卡表为一个比特位集合,每一个比特位可以用来表示老年代的某一个区域中所有对象是否持有新生代对象的引用.这样在新生代gc时候,不用花大量时间扫描所有老年代对象, 而是先扫描卡表. 只有标志为1才扫描这个区域,.
6.分区算法
分区算法将整个堆空间分成连续的不同小区间,每个小区间独立使用独立回收.这种算法可以控制一次回收多少个小区间.一般来说.堆空间越大,gc所需要的时间越长.所以每次只回收部分空间可以减少一次gc的停顿.
二.对象的引用类型
对象判断可触及性
1.可触及:根对象可达
2.可复活: 可以在finalize()复活.
3.不可触及: finalize被调用,没有复活.这种状态就会被回收.
public class FinalizeTest { public static FinalizeTest obj = null; @Override protected void finalize() throws Throwable { // TODO Auto-generated method stub super.finalize(); obj = this; } public static void main(String[] args) throws Exception { obj = new FinalizeTest(); obj = null; System.gc(); Thread.sleep(1000); if(obj == null){ System.out.println("对象为空"); }else{ System.out.println("对象不为空"); } System.out.println("第二次gc"); obj=null; System.gc(); Thread.sleep(1000); if(obj == null){ System.out.println("对象为空"); }else{ System.out.println("对象不为空"); } } }
引用和可触及性的强度
java提供了四种级别引用:强引用,弱引用,软引用,虚引用.
强引用:
String str = new String(“test”); String str1 = str;
这个两个都是强引用,它们具有以下特点:
强引用可以直接访问目标对象,不会被系统给回收,可能导致内存泄漏
软引用:可以被回收的引用,在内存不足的时候。
使用-xms10m测试 public class SoftReferenceTest { //占用15字节 public static class User { public String id = "1"; public String name="dfsdafasdf"; } public static void main(String[] args) { User user = new User(); SoftReference<User> test = new SoftReference<User>(user); user = null; System.out.println(test.get()); System.gc(); System.out.println("after Gc:"+test.get()); byte[] b = new byte[1024*983*7]; System.gc(); //内存不足,被回收了 System.out.println("after Gc1:"+test.get()); } }
每一个软引用都可以附带一个引用队列,当对象不可达,就会进入引用队列。
public class SoftReferenceQueueTest { public static class User { public String id = "1"; public String name="test"; @Override public String toString() { // TODO Auto-generated method stub return this.id; } } static ReferenceQueue<User> queue = null; public static class UserReference extends SoftReference<User>{ private String idd; public UserReference(User referent, ReferenceQueue<? super User> q) { super(referent, q); this.idd=referent.id; } } public static class ReferenceThread extends Thread{ @Override public void run() { while(true){ if(queue != null){ UserReference obj = null; try{ obj = (UserReference) queue.remove(); }catch (Exception e) { e.printStackTrace(); } if(obj !=null){ System.out.println("remove"+obj.idd); } } } } } public static void main(String[] args) { Thread t = new ReferenceThread(); t.setDaemon(true); t.start(); queue = new ReferenceQueue<User>(); User user = new User(); UserReference test = new UserReference(user,queue); user = null; System.out.println(test.get()); System.gc(); System.out.println("after Gc:"+test.get()); byte[] b = new byte[1024*983*7]; System.gc(); System.out.println("after Gc1:"+test.get()); } }
弱引用:发现就回收
一旦一个弱引用被回收,便会加入到一个注册的引用队列中
public class WeakReferenceTest { public static class User { public String id = "1"; public String name="dfsdafasdf"; } public static void main(String[] args) { User user = new User(); WeakReference<User> test = new WeakReference<User>(user); user = null; System.out.println(test.get()); System.gc(); System.out.println("after Gc:"+test.get()); byte[] b = new byte[1024*983*7]; System.gc(); System.out.println("after Gc1:"+test.get()); } }
虚引用:对象回收跟踪
虚引用是所有引用类型中最弱的。一个持有虚引用的对象,几乎和没有一样.当试图使用虚引用.get方法总是会失败.它必须和引用队列一起使用,用来跟踪垃圾回收过程.
public class PhantomReferenceTest { public static PhantomReferenceTest obj; @Override protected void finalize() throws Throwable { // TODO Auto-generated method stub super.finalize(); //复活这个对象 obj = this; } static ReferenceQueue<PhantomReferenceTest> queue = null; public static class ReferenceThread extends Thread { @Override public void run() { while (true) { if (queue != null) { PhantomReference<User> obj = null; try { obj = (PhantomReference) queue.remove(); } catch (Exception e) { e.printStackTrace(); } if (obj != null) { System.out.println("remove" + obj); } } } } } public static void main(String[] args) throws InterruptedException { Thread t = new ReferenceThread(); t.setDaemon(true); t.start(); queue = new ReferenceQueue<PhantomReferenceTest>(); obj = new PhantomReferenceTest(); PhantomReference<PhantomReferenceTest> test = new PhantomReference<PhantomReferenceTest>(obj, queue); obj = null; System.gc(); System.out.println("after Gc:"); Thread.sleep(1000); if(obj == null){ System.out.println("对象为空"); }else{ System.out.println("对象不为空"); } System.out.println("第二次gc"); obj=null; System.gc(); Thread.sleep(1000); if(obj == null){ System.out.println("对象为空"); }else{ System.out.println("对象不为空"); } } }
相关推荐
第四节:垃圾回收算法 1.1标记清除算法 1.2复制算法 1.3 标记整理(标记压缩)算法 第五节:垃圾回收器 1.1Serial/Serial Old收集器 1.2 ParNew收集器 1.3Parallel Scavenge收集器 1.4Parallel Old收集器 1.5CMS...
JVM垃圾回收算法总结 垃圾回收算法是Java虚拟机(JVM)中的一种机制,用于回收无用的对象以释放内存空间。垃圾回收算法可以从不同的角度划分,下面是常见的垃圾回收算法: 1. 引用计数(Reference Counting) ...
引用计数是一种较为古老且简单的垃圾回收算法。其基本思想是在每个对象中附带一个引用计数器,每当有一个地方引用它时,引用计数器的值就加一;当引用失效时,引用计数器的值减一。垃圾回收时只需要收集引用计数器值...
大部分新创建的对象首先被分配到Eden区,经历第一次垃圾回收后,存活下来的对象会转移到Survivor区。 - 老年代存放生命周期较长的对象,当新生代的空间不足以容纳新对象或者Survivor区的对象达到一定年龄时,它们...
### JVM垃圾回收(GC)详解 #### 一、对象回收判定方法 在Java虚拟机中,判断一个对象是否可以被回收通常有两种方法:**引用计数法**和**可达性分析法**。 ##### 引用计数法 引用计数法是一种较为简单的对象回收...
《垃圾回收系列(3):CLR与JVM垃圾回收器的比较》 本文主要探讨了.NET框架中的CLR(公共语言运行库)与Java平台中的JVM(Java虚拟机)在垃圾回收机制上的异同。垃圾回收是现代编程环境中管理内存的重要机制,它可以...
JVM性能调优-JVM内存整理及GC回收 JVM(Java Virtual Machine)性能调优是 Java ...JVM 性能调优需要深入理解 JVM 的工作机制和垃圾回收算法,然后根据实际情况选择合适的垃圾回收器和配置参数,以提高 JVM 的性能。
综上所述,JVM性能调优涉及多个方面,包括理解参数传递机制、掌握不同类型的引用以及深入了解垃圾回收算法和分区处理方式等。通过合理配置和调整这些机制,可以显著提高Java应用程序的性能和稳定性。开发者应根据...
"JVM调优总结" ...JVM调优是一种非常复杂的技术,需要我们对JVM的内部机理和垃圾回收算法有深入的理解。只有通过不断的学习和实践,我们才能更好地掌握JVM调优技术,提高Java应用程序的性能和稳定性。
1. 引用计数(Reference Counting):这是最古老的垃圾回收算法,原理是每个对象都有一个引用计数,当对象被引用时计数加一,当对象不再被引用时计数减一。垃圾回收时,只收集计数为 0 的对象。但是,这种算法无法...
JVM中主要采用以下几种垃圾回收算法: 1. **标记-清除算法**:这是最基础的收集算法,分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。 2. **复制算法**...
本文将详细讲解JVM如何判断对象已死的两种主要算法:引用计数算法和可达性分析算法,以及JDK 1.2之后引入的不同类型的引用。 首先,我们来看引用计数算法。这是一种简单的对象存活判断方法,为每个对象添加一个引用...
### JVM 运行时数据区域、垃圾回收机制与类加载机制详解 #### 一、运行时数据区域 Java虚拟机(JVM)的核心组件之一便是其运行时数据区域,这一区域负责存储程序运行过程中产生的各种数据。为了更好地理解这部分内容...
新创建的对象首先被放置在Eden区,经过第一次垃圾回收后,存活的对象会被移动到一个Survivor区。之后每次垃圾回收后,存活的对象会在两个Survivor区之间交换,并逐渐老化,最终进入老年代。 ### 3. 内存分代的目的...
2. 垃圾回收算法主要有标记清除、标记整理、复制算法。标记清除算法会留下内存碎片,但实现简单且速度较快;标记整理没有内存碎片,但速度较慢;复制算法没有内存碎片,速度快,但会暂时产生两倍的内存空间开销。 3...
第3讲 环境搭建以及jdk,jre,jvm的关系 免费 00:20:48 第4讲 jvm初体验-内存溢出问题的分析与解决 免费 00:17:59 第5讲 jvm再体验-jvm可视化监控工具 免费 00:21:17 第6讲 杂谈 免费 00:12:37 第7讲 ...
《JVM高级特性与最佳实践(第2版)》是一本深入探讨Java虚拟机(JVM)技术的书籍,其源代码提供了丰富的实践案例和示例,帮助读者更直观地理解JVM的工作原理和优化技巧。以下是根据书名和描述所涉及的一些关键知识点...
- 垃圾回收算法主要关注如何确定哪些对象是"存活"的,哪些是可回收的。两种主要的算法是引用计数法和可达性分析算法。 - 引用计数法:每个对象都有一个计数器,每当有一个引用指向它,计数器加一,引用失效时减一...
- JVM负责自动管理内存,通过垃圾收集机制回收不再使用的对象所占用的空间。 - 常见的垃圾收集算法有标记-清除、复制、标记-整理和分代收集等。 4. **类加载机制**: - 双亲委派模型:当一个类加载器需要加载类...