Jvm的垃圾回收机制到底会不会回收掉长时间不用的单例模式对象,这的确是一个比较有争议性的问题。将这一部分内容单独成篇的目的也是为了与广大博友广泛的讨论一下这个问题。为了能让更多的人看到这篇文章,请各位博友看完文章之后,点一下“顶”,让本篇文章排名尽量的靠前。笔者在此谢过。
讨论命题:当一个单例的对象长久不用时,会不会被jvm的垃圾收集机制回收。
首先说一下为什么会产生这一疑问,笔者本人再此之前从来没有考虑过垃圾回收对单例模式的影响,直到去年读了一本书,《设计模式之禅》秦小波著。在书中提到在j2ee应用中,jvm垃圾回收机制会把长久不用的单例类对象当作垃圾,并在cpu空闲的时候对其进行回收。之前读过的几本设计模式的书,包括《java与模式》,书中都没有提到jvm垃圾回收机制对单例的影响。并且在工作过程中,也没有过单例对象被回收的经历,加上工作中很多前辈曾经告诫过笔者:尽量不要声明太多的静态属性,因为这些静态属性被加载后不会被释放。因此对jvm垃圾收集会回收单例对象这一说法持怀疑态度。渐渐地,发现在同事中和网上的技术人员中,对这一问题也基本上是鲜明的对立两派。那么到底jvm会不会回收长久不用的单例对象呢。
对这一问题,笔者本人的观点是:不会回收。
下面给出本人的测试代码
- classSingleton{
- privatebyte[]a=newbyte[6*1024*1024];
- privatestaticSingletonsingleton=newSingleton();
- privateSingleton(){}
- publicstaticSingletongetInstance(){
- returnsingleton;
- }
- }
- classObj{
- privatebyte[]a=newbyte[3*1024*1024];
- }
- publicclassClient{
- publicstaticvoidmain(String[]args)throwsException{
- Singleton.getInstance();
- while(true){
- newObj();
- }
- }
- }
本段程序的目的是模拟j2ee容器,首先实例化单例类,这个单例类占6M内存,然后程序进入死循环,不断的创建对象,逼迫jvm进行垃圾回收,然后观察垃圾收集信息,如果进行垃圾收集后,内存仍然大于6M,则说明垃圾回收不会回收单例对象。
运行本程序使用的虚拟机是hotspot虚拟机,也就是我们使用的最多的java官方提供的虚拟机,俗称jdk,版本是jdk1.6.0_12
运行时vm arguments参数为:-verbose:gc -Xms20M -Xmx20M,意思是每次jvm进行垃圾回收时显示内存信息,jvm的内存设为固定20M。
运行结果:
……
[Full GC 18566K->6278K(20352K), 0.0101066 secs]
[GC 18567K->18566K(20352K), 0.0001978 secs]
[Full GC 18566K->6278K(20352K), 0.0088229 secs]
……
从运行结果中可以看到总有6M空间没有被收集。因此,笔者认为,至少在hotspot虚拟机中,垃圾回收是不会回收单例对象的。
后来查阅了一些相关的资料,hotspot虚拟机的垃圾收集算法使用根搜索算法。这个算法的基本思路是:对任何“活”的对象,一定能最终追溯到其存活在堆栈或静态存储区之中的引用。通过一系列名为根(GC Roots)的引用作为起点,从这些根开始搜索,经过一系列的路径,如果可以到达java堆中的对象,那么这个对象就是“活”的,是不可回收的。可以作为根的对象有:
- 虚拟机栈(栈桢中的本地变量表)中的引用的对象。
- 方法区中的类静态属性引用的对象。
- 方法区中的常量引用的对象。
- 本地方法栈中JNI的引用的对象。
方法区是jvm的一块内存区域,用来存放类相关的信息。很明显,java中单例模式创建的对象被自己类中的静态属性所引用,符合第二条,因此,单例对象不会被jvm垃圾收集。
虽然jvm堆中的单例对象不会被垃圾收集,但是单例类本身如果长时间不用会不会被收集呢?因为jvm对方法区也是有垃圾收集机制的。如果单例类被收集,那么堆中的对象就会失去到根的路径,必然会被垃圾收集掉。对此,笔者查阅了hotspot虚拟机对方法区的垃圾收集方法,jvm卸载类的判定条件如下:
- 该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例。
- 加载该类的ClassLoader已经被回收。
- 该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。
只有三个条件都满足,jvm才会在垃圾收集的时候卸载类。显然,单例的类不满足条件一,因此单例类也不会被卸载。也就是说,只要单例类中的静态引用指向jvm堆中的单例对象,那么单例类和单例对象都不会被垃圾收集,依据根搜索算法,对象是否会被垃圾收集与未被使用时间长短无关,仅仅在于这个对象是不是“活”的。假如一个对象长久未使用而被回收,那么收集算法应该是最近最长未使用算法,最近最长未使用算法一般用在操作系统的内外存交换中,如果用在虚拟机垃圾回收中,岂不是太不安全了?以上是笔者的观点。
因此笔者的观点是:在hotspot虚拟机1.6版本中,除非人为地断开单例中静态引用到单例对象的联接,否则jvm垃圾收集器是不会回收单例对象的。
期待各位博友的发言。
参考文献
Java虚拟机规范
Java hotspot虚拟机内存管理
Java编程思想
Java与模式
设计模式
设计模式之禅
深入理解java虚拟机
分享到:
相关推荐
接下来,我们讨论JVM的垃圾回收(Garbage Collection, GC): 1. 垃圾回收是自动管理内存的过程,JVM负责识别不再使用的对象并释放其占用的内存空间。 2. GC主要分为新生代、老年代和持久代,不同区域采用不同的收集...
Go语言,作为Google开发的一种静态类型、编译型、并发型、垃圾回收的语言,也有一系列适用于其特性的设计模式。这份“精选的Go设计模式、食谱和习惯用法列表”是一个宝贵的资源集合,它涵盖了Go语言中广泛使用的最佳...
- **垃圾回收算法**:105-虚拟机-05-jvm垃圾回收算法讨论了几种常见的垃圾回收算法,如标记-清除、复制、标记-整理和分代收集等。 3. **设计模式** - **单例模式**:56-基础篇-56-单例模式_方式1_饿汉式介绍了...
Java是一种面向对象的编程语言,它的特点包括平台独立性(通过Java虚拟机JVM实现)、垃圾回收机制、自动内存管理以及丰富的类库。学习J2SE时,你将接触以下核心概念: 1. **语法基础**:包括数据类型(如整型、浮点...
- **单例模式**:掌握多种实现单例的方式及其优缺点。 - **装饰器模式**:如何在不改变原有类的基础上增加功能。 - **观察者模式**:理解事件驱动编程和发布/订阅模式。 6. **框架** - **Spring框架**:讲解...
- **单例模式**:掌握多种实现单例的方式,如饿汉式、懒汉式、双重检查锁定等。 - **工厂模式**:理解简单工厂、工厂方法和抽象工厂模式的应用。 - **装饰者模式**:知道如何动态地给对象增加功能,而保持其接口...
- 讲解Java的内存管理,包括垃圾回收机制、对象生命周期和内存泄漏的概念。 - 讨论Java的数据类型,尤其是引用类型与原始类型的差异。 - 介绍多线程,包括线程同步、死锁、线程池和并发集合的使用。 2. 面向对象...
- **单例模式**:讨论常见的单例实现方式及其优缺点。 - **工厂模式**:了解简单工厂、工厂方法和抽象工厂的区别。 - **装饰者模式**:用于动态扩展功能的模式。 - **代理模式**:在设计模式中的应用及其实现。 ...
1. **内存管理**:Java的垃圾回收机制虽然自动化程度高,但过度创建对象或持有大量短生命周期的对象可能会导致频繁的垃圾回收,影响性能。合理地设计对象池,使用`StringBuilder`而非`String`进行字符串拼接,以及...
2. 未释放的单例对象:单例模式的实现不当,如全局静态单例对象持有大量资源或引用其他对象,可能导致内存泄漏。 3. 外部线程引用:当一个线程被某个外部对象引用,即使线程已经完成其工作,也无法被垃圾收集器回收...
- **创建型模式**:单例模式、工厂模式(简单工厂、抽象工厂、工厂方法)、建造者模式、原型模式,这些模式用于管理对象的创建过程。 - **结构型模式**:适配器模式、装饰器模式、代理模式、桥接模式、组合模式、...
- **单例模式**:介绍单例模式的实现方式,如饿汉式、懒汉式、双重检查锁定等。 - **工厂模式**:讲解简单工厂、工厂方法和抽象工厂模式的区别和应用场景。 7. **IO流** - **字符流与字节流**:区分两者的处理...
- **单例模式**:面试中可能要求编写不同类型的单例实现,例如内部类实现和双检查懒汉模式,并解释volatile关键字在此中的作用,防止指令重排和确保可见性。 - **线程内存模型**:面试官可能会要求详细解释Java...
7. **单例模式**:面试者被要求编写了两种单例模式,内部类单例和双检查懒汉单例,同时也解释了volatile关键字在并发中的作用,防止指令重排,确保多线程环境下的正确性。 8. **线程知识**:面试官可能进一步询问了...
- **CLR与GC**:了解Common Language Runtime (CLR)的工作原理和垃圾回收机制。 - **命名空间与程序集**:掌握如何使用命名空间组织代码,理解程序集的编译与加载过程。 - **异常处理**:学习try-catch-finally...
- **内存管理**:理解栈和堆的区别,以及垃圾回收机制的工作原理。 2. **Java集合框架**: - **ArrayList与LinkedList**:理解两者的实现方式和性能特点,知道何时选择哪种。 - **HashMap与HashTable**:比较...
Java的特点包括自动内存管理、垃圾回收机制、丰富的类库以及强大的异常处理机制。学习Java不仅意味着掌握语法,还包括理解其内存模型、多线程编程、网络编程以及I/O操作等核心概念。 设计模式,另一方面,是软件...
- **内存管理**:理解堆和栈的区别,垃圾回收机制的工作原理。 - **异常处理**:掌握try-catch-finally语句块,以及不同类型的异常。 2. **数据类型和集合框架** - **基本类型与引用类型**:理解两者的区别和...
- **单例模式**:学习如何创建只有一个实例的类,并理解其在多线程环境下的实现方式。 - **工厂模式**:了解如何使用工厂模式来抽象对象的创建过程。 - **观察者模式**:学习事件驱动编程,理解Observer接口和被...