通过关键字new创建java对象,即可视为java对象申请内存空间,JVM会在内存堆中为每个对象分配空间,当一个java对象失去引用时,JVM的垃圾回收机制会自动清除他们,并回收它们所占的内存空间。
是否回收一个对象的标准:是否还有引用变量引用该对象
JVM的垃圾回收机制采用有向图方式来管理内存中的对象。对象在堆内存中的三种状态: 1. 可达状态:有一个以上的引用变量引用它, 在有向图中可以从起始顶点导航到该对象, 程序可通过引用变量来调用该对象的属性和方法 2. 可恢复状态:某个对象不再被任何对象引用,从起始顶点不能导航到该对象,在回收该对象之前,系统会调用finalize方法进行资源清理,如果系统在调用finalizde后重新让一个以上的引用变量引用该对象,则再次变为可达状态,反之为不可达状态 3.不可达状态:对象的所有关联被切断,且调用finalize后仍为不可达,即对象永久失去引用
当某个对象被其他类的类变量引用时,只有该类被销毁后该对象才会进入可恢复状态;当某个对象被其他对象的实例变量引用时,只有当引用该对象的对象被销毁或变为不可达之后才会能进入不可达状态。
强引用:程序创建一个对象,并把这个对象赋给一个引用变量 软引用:用SoftReference类加以实现,当系统空间足够时不会被系统回收,当系统空间不足时会被回收 弱引用:通过WeakReference类实现,和软引用很像,但弱引用的引用级别更低。当系统回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存。 虚引用:跟踪对象被垃圾回收的状态,程序可以通过检查与虚引用关联的引用队列中是否已经包含指定的虚引用,从而了解虚引用所引用对象是否即将被回收。虚引用必须和引用队列联合使用。
内存泄漏:存在无用的额内存没有被回收回来 java内存泄漏的原因: JVM肯定不会回收强引用所引用的java对象,即使系统内存非常紧张,即使该对象以后永远不会被用到 产生的原因:所有不可达的对象都由垃圾回收机负责回收,因此程序猿不需考虑这部分的内存泄漏。但如果程序中的一些java对象处于可达状态,但程序以后永远都不会访问他们,则占据的内存空间不会被回收,因此发生内存泄漏。
垃圾回收机制完成的两件事: 1. 跟踪并监控每个java对象,当某个对象处于不可达状态时,回收该对象所占用的内存; 2. 清理内存分配、回收过程中产生的内存碎片
垃圾回收的设计算法: 1. 串行回收:始终使用一个CPU来执行垃圾回收操作 2. 并行回收:每个CPU负责回收工作的一部分,效率高,同时复杂度也在提升,内存碎片会增加 3. 应用程序停止:执行垃圾回收的同时会导致应用程序的暂停 4. 并发执行:不会导致应用程序停止,需解决和应用程序的执行冲突,开销比应用程序停止的方法大,需更多的堆内存 5. 复制式:将所有可达对象复制到另一块相同的内存中,然后一次性回收整个空间,不产生内存碎片,但需要额外的内存 6. 不压缩式: 从根访问所有可达对象并标记,再次遍历内存区域,回收处理未标记的对象,内存利用率高,但需遍历两次堆内存,易产生碎片 7. 压缩式:从根访问所有可达对象并标记,将所有可达对象搬迁到一起,之前占用的内存全部回收
现行的垃圾回收器采用分代的方式来进行回收,根据对象生存时间的长短分为young、old、permanent,基于以下两点事实: 1. 绝大多数的对象在young期间就会被回收; 2. 很老的对象和很新的对象很少存在相互引用
young代采用复制算法,因为大部分对象很快进入不可达状态。只需遍历那些处于可达状态的对象,而且这些对象的数量较少,复制成本不大,因此可充分发挥复制算法的优点。 young代由1个Eden区和两个Survivor区构成。绝大多数的对象先分配到Eden区,Survivor区中的对象至少在young代中经历了一次垃圾回收,同一时间1个survivor用来保存对象,另一个是空的。复制就是将Eden和非空的survivor复制到另一个survivor
old代垃圾回收具有如下两个特征: 1. old代垃圾回收的执行频率无需太高,因此很少有对象会死掉 2. 每次对old代执行垃圾回收需要更长的时间来完成 基于以上考虑,会采用标记压缩算法,可避免复制old代的大量对象,而且由于old代的对象不会很快死亡,回收过程在并不会产生大量的内存碎片
permanent代中的对象垃圾回收机制通常不会回收
总结: young代回收的系统开销比较小,也被称为次要回收。old代回收的成本较大,称为主要回收。young代的内存会先被回收,对于old代的回收频率则要低的多。
常见的垃圾回收器: 1. 串行回收器:young代和old代的回收都是串行的 2. 并行回收器:比串行回收器增加了多CPU并行的能力,即同时启动多线程并行来执行垃圾回收,并行只针对young代的复制算法 3.并行压缩回收器:会old代采用不同的算法(先划分几个固定大小的区域,mark阶段多线程标记可达对象,summary阶段从最左边的区域检查每个区域的密度,当检测到可达密度较小时会对其和右侧区域进行回收,comact阶段利用多线程并行复制数据到密集区域) 4.并发标示-清理回收器(CMS):young代回收方式保持不变,CMS对多数old代回收为并发操作,可分为初始标识、并发标识和再标识。只需两次短暂的暂停,适用于对实时性要求较高的程序,但需要的堆内存也是最大的
内存管理小技巧: 1. 直接采用直接量创建,不用new的方式来创建 2. 尽早释放无用对象的引用:方法局部变量引用所引用的对象会随着方法的结束变为垃圾,并很快被JVM回收。 3. 尽量少用静态变量:类变量会存放到permanent代中,只要类对象存在就不会被垃圾回收 4. 避免在经常调用的方法、循环中创建java对象 5.缓存经常使用的对象:使用HashMap进行缓存,直接使用某些开源的缓存项目 6. 尽量不要使用finalize的方法 7. 考虑使用软引用:当系统内存紧张时,系统会自动释放软引用所引用的对象
新闻热点
疑难解答