`
弦上的福音
  • 浏览: 21805 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

java对象在jvm中的生命周期(转)

    博客分类:
  • java
阅读更多

在JVM运行空间中,对象的整个生命周期大致可以分为7个阶段:创建阶段(Creation)、应用阶段(Using)、不可视阶段(Invisible)、不可到达阶段(Unreachable)、可收集阶段(Collected)、终结阶段(Finalized)与释放阶段(Free)。上面的这7个阶段,构成了 JVM中对象的完整的生命周期。下面分别介绍对象在处于这7个阶段时的不同情形。

2.2.1 创建阶段

在对象创建阶段,系统要通过下面的步骤,完成对象的创建过程:

(1)为对象分配存储空间。

(2)开始构造对象。

(3)递归调用其超类的构造方法。

(4)进行对象实例初始化与变量初始化。

(5)执行构造方法体。

上面的5个步骤中的第3步就是指递归地调用该类所扩展的所有父类的构造方法,一个Java类(除Object类外)至少有一个父类(Object),这个规则既是强制的,也是隐式的。你可能已经注意到在创建一个Java类的时候,并没有显式地声明扩展(extends)一个Object父类。实际上,在 Java程序设计中,任何一个Java类都直接或间接的是Object类的子类。例如下面的代码:

public class A {

}

这个声明等同于下面的声明:

public class A extends java.lang.Object {

}

上面讲解了对象处于创建阶段时,系统所做的一些处理工作,其中有些过程与应用的性能密切相关,因此在创建对象时,我们应该遵循一些基本的规则,以提高应用的性能。

下面是在创建对象时的几个关键应用规则:

(1)避免在循环体中创建对象,即使该对象占用内存空间不大。

(2)尽量及时使对象符合垃圾回收标准。

(3)不要采用过深的继承层次。

(4)访问本地变量优于访问类中的变量。

关于规则(1)避免在循环体中创建对象,即使该对象占用内存空间不大,需要提示一下,这种情况在我们的实际应用中经常遇到,而且我们很容易犯类似的错误,例如下面的代码:

… …

for (int i = 0; i < 10000; ++i) {

Object obj = new Object();

System.out.println("obj= "+ obj);

}

… …

上面代码的书写方式相信对你来说不会陌生,也许在以前的应用开发中你也这样做过,尤其是在枚举一个Vector对象中的对象元素的操作中经常会这样书写,但这却违反了上述规则(1),因为这样会浪费较大的内存空间,正确的方法如下所示:

… …

Object obj = null;

for (int i = 0; i < 10000; ++i) {

obj = new Object();

System.out.println("obj= "+ obj);

}

… …

采用上面的第二种编写方式,仅在内存中保存一份对该对象的引用,而不像上面的第一种编写方式中代码会在内存中产生大量的对象应用,浪费大量的内存空间,而且增大了系统做垃圾回收的负荷。因此在循环体中声明创建对象的编写方式应该尽量避免。

另外,不要对一个对象进行多次初始化,这同样会带来较大的内存开销,降低系统性能,如:

public class A {

private Hashtable table = new Hashtable ();

public A() {

// 将Hashtable对象table初始化了两次

table = new Hashtable();

}

}

正确的方式为:

public class B {

private Hashtable table = new Hashtable ();

public B() {

}

}

不要小看这个差别,它却使应用软件的性能相差甚远.

 

 

看来在程序设计中也应该遵从“勿以恶小而为之 ”的古训,否则我们开发出来的应用也是低效的应用,有时应用软件中的一个极小的失误,就会大幅度地降低整个系统的性能。因此,我们在日常的应用开发中,应该认真对待每一行代码,采用最优化的编写方式,不要忽视细节,不要忽视潜在的问题。

2.2.2 应用阶段

当对象的创建阶段结束之后,该对象通常就会进入对象的应用阶段。这个阶段是对象得以表现自身能力的阶段。也就是说对象的应用阶段是对象整个生命周期中证明自身“存在价值”的时期。在对象的应用阶段,对象具备下列特征:

— 系统至少维护着对象的一个强引用(Strong Reference);

— 所有对该对象的引用全部是强引用(除非我们显式地使用了:软引用(Soft Reference)、弱引用(Weak Reference)或虚引用(Phantom Reference))。

上面提到了几种不同的引用类型。可能一些读者对这几种引用的概念还不是很清楚,下面分别对之加以介绍。在讲解这几种不同类型的引用之前,我们必须先了解一下Java中对象引用的结构层次。

所提到的几种引用的层次关系,其中强引用处于顶端,而虚引用则处于底端。下面分别予以介绍。

1.强引用

强引用(Strong Reference)是指JVM内存管理器从根引用集合(Root Set)出发遍寻堆中所有到达对象的路径。当到达某对象的任意路径都不含有引用对象时,对这个对象的引用就被称为强引用。

2.软引用

软引用(Soft Reference)的主要特点是具有较强的引用功能。只有当内存不够的时候,才回收这类内存,因此在内存足够的时候,它们通常不被回收。另外,这些引用对象还能保证在Java抛出OutOfMemory 异常之前,被设置为null。它可以用于实现一些常用资源的缓存,实现Cache的功能,保证最大限度的使用内存而不引起OutOfMemory。再者,软可到达对象的所有软引用都要保证在虚拟机抛出OutOfMemoryError之前已经被清除。否则,清除软引用的时间或者清除不同对象的一组此类引用的顺序将不受任何约束。然而,虚拟机实现不鼓励清除最近访问或使用过的软引用。下面是软引用的实现代码:

… …

import java.lang.ref.SoftReference;

A a = new A();

// 使用 a

// 使用完了a,将它设置为soft 引用类型,并且释放强引用;

SoftReference sr = new SoftReference(a);

a = null;

// 下次使用时

if (sr!=null) {

a = sr.get();

}

else{

// GC由于内存资源不足,可能系统已回收了a的软引用,

// 因此需要重新装载。

a = new A();

sr=new SoftReference(a);

}

… …

软引用技术的引进,使Java应用可以更好地管理内存,稳定系统,防止系统内存溢出,避免系统崩溃(crash)。因此在处理一些占用内存较大而且声明周期较长,但使用并不频繁的对象时应尽量应用该技术。正像上面的代码一样,我们可以在对象被回收之后重新创建(这里是指那些没有保留运行过程中状态的对象),提高应用对内存的使用效率,提高系统稳定性。但事物总是带有两面性的,有利亦有弊。在某些时候对软引用的使用会降低应用的运行效率与性能,例如:应用软引用的对象的初始化过程较为耗时,或者对象的状态在程序的运行过程中发生了变化,都会给重新创建对象与初始化对象带来不同程度的麻烦,有些时候我们要权衡利弊择时应用。

3.弱引用

弱引用(Weak Reference)对象与Soft引用对象的最大不同就在于:GC在进行回收时,需要通过算法检查是否回收Soft引用对象,而对于Weak引用对象,GC总是进行回收。因此Weak引用对象会更容易、更快被GC回收。虽然,GC在运行时一定回收Weak引用对象,但是复杂关系的Weak对象群常常需要好几次GC的运行才能完成。Weak引用对象常常用于Map数据结构中,引用占用内存空间较大的对象,一旦该对象的强引用为null时,对这个对象引用就不存在了,GC能够快速地回收该对象空间。与软引用类似我们也可以给出相应的应用代码:

… …

import java.lang.ref.WeakReference;

A a = new A();

// 使用 a

// 使用完了a,将它设置为weak 引用类型,并且释放强引用;

WeakReference wr = new WeakReference (a);

a = null;

// 下次使用时

if (wr!=null) {

a = wr.get();

}

else{

a = new A();

wr = new WeakReference (a);

}

… …

弱引用技术主要适用于实现无法防止其键(或值)被回收的规范化映射。另外,弱引用分为“短弱引用(Short Week Reference)”和“长弱引用(Long Week Reference)”,其区别是长弱引用在对象的Finalize方法被GC调用后依然追踪对象。基于安全考虑,不推荐使用长弱引用。因此建议使用下面的方式创建对象的弱引用。

… …

WeakReference wr = new WeakReference(obj);

WeakReference wr = new WeakReference(obj, false);

… …

4.虚引用

虚引用(Phantom Reference)的用途较少,主要用于辅助finalize函数的使用。Phantom对象指一些执行完了finalize函数,并且为不可达对象,但是还没有被GC回收的对象。这种对象可以辅助finalize进行一些后期的回收工作,我们通过覆盖Reference的clear()方法,增强资源回收机制的灵活性。虚引用主要适用于以某种比 Java 终结机制更灵活的方式调度 pre-mortem 清除操作。

&注意 在实际程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速JVM对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。

2.2.3 不可视阶段

在一个对象经历了应用阶段之后,那么该对象便处于不可视阶段,说明我们在其他区域的代码中已经不可以再引用它,其强引用已经消失,例如,本地变量超出了其可视范围,如下所示。

… …

public void process () {

try {

Object obj = new Object();

obj.doSomething();

} catch (Exception e) {

e.printStackTrace();

}

while (isLoop) { // ... loops forever

// 这个区域对于obj对象来说已经是不可视的了

// 因此下面的代码在编译时会引发错误

obj.doSomething();

}

}

… …

如果一个对象已使用完,而且在其可视区域不再使用,此时应该主动将其设置为空(null)。可以在上面的代码行obj.doSomething();下添加代码行obj = null;,这样一行代码强制将obj对象置为空值。这样做的意义是,可以帮助JVM及时地发现这个垃圾对象,并且可以及时地回收该对象所占用的系统资源。

2.2.4 不可到达阶段

处于不可到达阶段的对象,在虚拟机所管理的对象引用根集合中再也找不到直接或间接的强引用,这些对象通常是指所有线程栈中的临时变量,所有已装载的类的静态变量或者对本地代码接口(JNI)的引用。这些对象都是要被垃圾回收器回收的预备对象,但此时该对象并不能被垃圾回收器直接回收。其实所有垃圾回收算法所面临的问题是相同的——找出由分配器分配的,但是用户程序不可到达的内存块。

2.2.5 可收集阶段、终结阶段与释放阶段

对象生命周期的最后一个阶段是可收集阶段、终结阶段与释放阶段。当对象处于这个阶段的时候,可能处于下面三种情况:

(1)垃圾回收器发现该对象已经不可到达。

(2)finalize方法已经被执行。

(3)对象空间已被重用。

当对象处于上面的三种情况时,该对象就处于可收集阶段、终结阶段与释放阶段了。虚拟机就可以直接将该对象回收了。

分享到:
评论

相关推荐

    Java对象在JVM中的生命周期详解

    "Java对象在JVM中的生命周期详解" Java对象在JVM中的生命周期是Java编程语言中一个非常重要的概念,它涉及到Java对象的创建、使用、释放和销毁整个过程。在JVM中,Java对象的生命周期可以分为七个阶段:创建阶段、...

    JVM中对象的生命周期

    在Java虚拟机(JVM)中,对象的生命周期包含了多个阶段,这些阶段共同决定了一个对象从诞生到消亡的过程。以下是这些阶段的详细介绍: **创建阶段(Creation)** 在这个阶段,对象从无到有,主要经历以下几个步骤:...

    Java中类的生命周期

    Java程序与JVM生命周期的一致性** 程序生命周期与Java虚拟机生命周期是一致的。这意味着Java虚拟机进程从创建起的任务就是执行Java程序,直至程序正常退出或异常终止,此时JVM也会随之关闭。 #### 二、类的生命...

    jvmjava,java实现的JVM。.zip

    这有助于深入理解JVM的生命周期管理、异常处理、多线程等复杂概念。 五、优化与进阶 掌握JVM的工作原理后,开发者可以进行更高级的调优,如调整JVM参数以优化内存分配、提高垃圾收集效率等。此外,还可以探索JVM的...

    java11-jvm白皮书

    2. 分代内存模型:JVM将内存分为新生代、老年代和持久代,根据对象生命周期的不同,进行不同策略的垃圾收集,提高了内存利用率和程序性能。 三、类加载机制 1. 类加载器:Java 11中,JVM的类加载器系统确保了类的...

    Java虚拟机和Java程序的生命周期?

    ### Java虚拟机与Java程序的生命周期 #### 一、Java虚拟机(JVM)概述 Java虚拟机(JVM)是一种可以执行Java字节码的虚拟机。它为Java应用程序提供了一个独立于硬件平台的运行环境,使得Java程序可以在任何安装了JVM...

    java-jvm虚拟机原理.ppt

    首先,JVM的生命周期与Java程序紧密关联。每当运行一个Java程序时,就会创建一个JVM实例。这个实例在程序执行期间存在,直到程序结束或通过调用`Runtime.getRuntime().exit()`或`System.exit()`方法显式退出。JVM...

    Java对象的生命周期[参照].pdf

    在Java对象的生命周期中,装载阶段是由类加载器完成的。Java的类加载器系统由Bootstrap ClassLoader、Extension ClassLoader和System ClassLoader组成,它们分别负责加载不同层次的类库和用户自定义类。用户还可以...

    java虚拟机(jvm)介绍以及相关参数设置与调优

    在本文中,我们将深入探讨Java虚拟机的体系结构、垃圾回收机制、Java对象的生命周期和分代,以及相关的参数设置与调优。 Java虚拟机体系结构 Java虚拟机由三个主要组件组成:类加载器、运行时数据区和执行引擎。类...

    java基础之JVM

    Java堆是JVM管理的最大一块内存区域,主要用于存放所有Java对象实例以及数组。根据对象存活周期的不同,堆又被划分为新生代(Young Generation)和老年代(Old Generation)两部分。新生代又被细分为Eden区和两个...

    java中jvm内存分配相关资料总结整理

    如果对象很小且生命周期短,也可能在栈上创建,但这取决于JVM的具体实现。 - **栈内存**:栈内存主要存储方法的局部变量,包括基本类型和引用类型。一旦方法执行完毕,这些变量就会被自动释放。 - **堆内存**:堆...

    Java虚拟机-jvm故障诊断与性能优化-源码

    - **堆内存**:Java对象主要存储在堆中,分为新生代、老年代和永久代(或元空间)。新生代又细分为Eden和两个Survivor区,用于对象的创建和快速回收。 - **栈内存**:每个线程都有一个独立的栈,用于存储方法的...

    JVM JMX java

    1. **堆内存管理**:Java对象主要存储在堆内存中,JVM通过新生代、老年代、永久代(在Java 8之后变为元空间)等分区来优化垃圾回收。 2. **栈内存管理**:每个线程都有一个独立的栈,用于存储方法调用帧,包括局部...

    Java语言规范和JVM规范官网文档

    另外,JVM的类加载机制涉及到类的生命周期,包括加载、验证、准备、解析和初始化。理解这个过程对于解决类加载问题、实现自定义类加载器或者进行动态代理等高级技术都有很大帮助。 在Java9中,JVM进行了模块化改革...

    Java中类的生命周期.doc

    在Java编程语言中,类的生命周期是从类被加载到Java虚拟机(JVM)中开始,经过一系列处理直至类被卸载的过程。这一过程中涉及的关键步骤包括类的加载、连接、初始化等。理解类的生命周期对于深入掌握Java内存管理和...

    Java中类与对象的生命周期

    Java中的类与对象的生命周期是理解Java运行机制的关键部分,涉及到类加载、连接、初始化、使用和卸载等环节。让我们深入探讨这些概念。 首先,类的生命周期始于.java源文件被编译成.class字节码文件。Java虚拟机...

    java面向对象之JVM创建及分配策略方法详解.docx

    逃逸分析是JVM的一种优化技术,用于判断对象的生命周期和作用范围。如果对象在创建后只在当前作用域内使用,没有被其他方法或线程访问,那么JVM可能会选择栈上分配。这样做可以减少堆的压力,提高性能,因为栈上的...

    深入Java虚拟机 JVM pdf

    堆内存是Java对象的主要存储区域,而栈则用于存储方法调用时的局部变量。方法区则是用来存放类信息、常量、静态变量等。理解这些区域如何工作有助于我们优化内存使用,避免常见的内存溢出问题。 垃圾收集是JVM内存...

Global site tag (gtag.js) - Google Analytics