Java内存管理

来源:互联网 发布:数据新闻手册 pdf 编辑:程序博客网 时间:2024/05/22 07:47

1 内存管理
垃圾回收(Garbage Collection,GC)是java中内存管理的核心概念,JVM的内存管理机制被称为垃圾回收机制。

1.1 堆内存(heap)与栈内存(stack)
从堆和栈的功能和作用来通俗的比较,堆主要用来存放对象的,栈主要是用来执行程序的.而这种不同又主要是由于堆和栈的特点决定的:
在编程中,例如C/C++中,所有的方法调用都是通过栈来进行的,所有的局部变量,形式参数都是从栈中分配内存空间的。实际上也不是什么分 配,只是从栈顶向上用就行,就好像工厂中的传送带(conveyor belt)一样,Stack Pointer会自动指引你到放东西的位置,你所要做的只是把东西放下来就行.退出函数的时候,修改栈指针就可以把栈中的内容销毁.这样的模式速度最快, 当然要用来运行程序了.需要注意的是,在分配的时候,比如为一个即将要调用的程序模块分配数据区时,应事先知道这个数据区的大小,也就说是虽然分配是在程序运行时进行的,但是分配的大小多少是确定的,不变的,而这个”大小多少”是在编译时确定的,不是在运行时.
堆是应用程序在运行的时候请求操作系统分配给自己内存,由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间,因此用堆的效率非常 低.但是堆的优点在于,编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间,因此,用堆保存数据时会得到更大的灵活 性。事实上,面向对象的多态性,堆内存分配是必不可少的,因为多态变量所需的存储空间只有在运行时创建了对象之后才能确定.

1.2 堆内存(heap)被分为两个区域:新对象区域与老对象区域。
JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to)。一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中。
在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor 区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他 们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达 到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新 的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。 Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。
垃圾回收过程示意

2 JVM中对象的生命周期

2.1 创建阶段
1)为对象分配存储空间
2)开始构造对象
3)递归调用其超类的构造方法
4)进行对象实例初始化与变量初始化
5)执行构造方法体

2.2 应用阶段
强引用(Strong Reference)是最普遍的引用,即使内存空间不足也不会随意回收强引用的对象。
软引用(Soft Reference)只有当内存不够的时候才回收这类内存。它可以用于实现一些常用资源的缓存,实现Cache功能,保证最大限度地使用内存而不引起OutOfMemory。

import java.lang.ref.SoftReference;A a = new A();//use a//After using a, set soft-referenceSoftReference sr = new SoftReference(a);a = null;//the next useif(sr != null){    a = sr.get();}else{    a = new A();    sr = new SoftReference(a);}

软引用使得JVM可以更好管理内存,稳定系统。在处理一些占用内存较大而且声明周期较长,但使用并不频繁的对象时尽量使用该技术。
弱引用(Weak Reference)与Soft引用对象的最大不同就在于:GC在进行回收时,需要通过算法检查是否回收Soft引用对象,而对于Weak引用对象, GC总是进行回收。与软引用的使用方法类似(import java.lang.ref.WeakReference)。
虚引用(Phantom Reference)主要用于辅助finalize函数的使用。
实际应用中很少使用弱引用和虚引用,使用软引用的情况较多。

2.3不可视阶段、不可到达阶段、可收集阶段、终结阶段与释放阶段

3析构方法finalize
理论上JVM负责对象的析构工作也就是垃圾回收,其实java中finalize方法类似于C++中的析构函数。finalize是根基类Object的一个protected方法,由于finalize没有自动实现递归调用,因此finalize函数的最后一个语句通常是super.finalize()。
finalize方法最终是由JVM中的垃圾回收器调用的,由于垃圾回收器调用finalize的时间是不确定或者不及时的,调用时机对我们来说是不可控 的,因此,有时我们需要通过其他的手段来释放程序中所占用的系统资源,比如自己在类中声明一个destroy()方法,在这个方法中添加释放系统资源的处 理代码,当你使用完该对象后可以通过调用这个destroy()方法来释放该对象内部成员占用的系统资源。通常我们在finalize方法中释放一些不容易控制、并且非常重要的资源,例如一些I/O操作,数据连接。在类深度继承的情况下,可以通过递归调用destroy方法在子类被销毁的时候释放父类占用的资源,
1、原始基类A

public class A {      Object a = null;      public A() {          a = new Object();          System.out.println("创建a对象");      }       protected void destroy() {         System.out.println("释放a对象");         a = null;         // 释放自身所占用的资源         …      }      protected void finalize() throws java.lang.Throwable {         destroy();         // 递归调用超类中的finalize方法         super.finalize();        }}

2.一级子类B

public  class B extends A {    Object b = null;    public B() {          b = new Object();          System.out.println("创建b对象");    }     protected void destroy() {         b = null;         // 释放自身所占用的资源         System.out.println("释放b对象");         super.destroy();    }    protected void finalize() throws java.lang.Throwable {         destroy();         // 递归调用超类中的finalize方法         super.finalize();      }}

3.二级子类C

public class C extends B {    Object c = null;    public C() {     c = new Object();         System.out.println("创建c对象");    }     protected void destroy() {         c = null;         // 释放自身所占用的资源         System.out.println("释放c对象");         super.destroy();    }    protected void finalize()throws java.lang.Throwable {         destroy();         // 递归调用超类中的finalize方法         super.finalize();      }}

4.数组创建
数组的显式申请指直接给出数组的类型和长度。而隐式申请是指声明数组对象时不知道具体长度,不存在浪费内存的问题。遇到数组保存元素占用内存空间较大或数组本身长度较大的情况,可以采用软引用技术来引用数组以提醒JVM及时回收。

5.共享静态变量存储空间
当类中某变量是常量,则可以声明为静态变量,实现内存空间对对象实例的共享,来节省系统内存开销。但是静态变量生命周期较长,不易被系统回收,当全部满足下列条件的情况下尽量使用静态变量:
1) 变量包含对象体积较大,占用内存较多。
2) 变量所包含对象生命周期较长。
3) 变量所包含对象数据稳定。
4) 该类的对象实例有对该变量所包含对象的共享需求。

附说明:
本文是读Java优化编程这本书之后自己总结了几个与java内存管理的点,便于自己理解和回顾。

附一个无关注意点……
一般情况下,都不要将类中的变量声明为public类型,一般通过public类型方法来访问相关变量,例如:getXX()。
当试图在代码中引用类的静态方法或静态成员变量时,不通过该类的实例对象引用,即使这样是允许的,应该通过类来引用他们。
myClass.classMethod();//正确
myObject.classMethod();//不正确

0 0