我们在深入Java核心系列文章中给大家讲过JVM中的栈和局部变量在做Java开发的时候常用的JVM内存管理有两种一种是堆内存一种是栈内存堆内存主要用来存储程序在运行时创建或实例化的对象与变量例如我们通过new MyClass()创建的类MyClass的对象而栈内存则是用来存储程序代码中声明为静态(或非静态)的方法下面我给大家举个例子 就拿上面的例子来说放在栈内存中的有mainmakeThings放在堆内存中有Testlistobject JVM中对象的生命周期大致可以分为个阶段创建阶段应用阶段不可视阶段不可到达阶段可收集阶段终结阶段与释放阶段 创建阶段()为对象分配存储空间 ()开始构造对象 ()递归调用其超类的构造方法 ()进行对象实力初始化与变量初始化 ()执行构造方法体 还有就是你在创建对象的时候需要注意的地方 ()避免在循环体中创建对象即使该对象占用内存空间不大 ()尽量及时使对象符合垃圾回收标准 ()不要采用过深的继承层次 ()访问本地变量优于访问类中的变量 应用阶段在应用阶段涉及到个引用 ()强引用是指JVM内存管理器从根引用集合出发遍寻堆中所有到达对象的路径 ()软引用是具有较强的引用功能只有当内存不够的时候才回收这类内存因此内存足够的时候不会被回收 ()弱引用弱引用与软引用对象的最大不同在于GC在进行回收时需要通过算法检查是否回收软引用对象而对于弱引用来说GC总是进行回收 ()虚引用主要用于辅助finalize函数的使用虚引用主要适用于以某种比Java终结机制更灵活的方式调度premortem清除操作 不可视阶段先看一段代码 如果一个对象已使用完了应该主动将其设置为null可以在上面的代码行objdoSomething();下添加代码行obj=null;这样一行代码强制将obj对象置为空值这样做的意义就是帮助JVM及时的发现这个垃圾对象并且可以及时的回收该对象占用的系统资源 不可到达阶段处于不可到达阶段的对象在虚拟机所管理的对象引用根集合中再也找不到直接或间接的强引用这些对象通常是指多有线程栈中的临时变量所有已装载的类的静态变量或者对本地代码接口(JNI)引用 可收集阶段终结阶段与释放阶段 当对象处于这个阶段的时候可能处于下面三种情况 ()垃圾回收器发现该对象已经不可到达 ()finalize方法已经被执行 ()对象空间已被重用 当对象处于上面三种清空的时候虚拟机就可以直接将该对象回收了 析构方法finalize前面我们说了JVM的垃圾回收机制和JVM中对象的生命周期今天给大家讲个方法叫做析构方法finalize我想搞过C++的人都知道而且是内存管理技术中相当重要的一部分但是在Java中好像没有这个概念这是因为理论上JVM负责对象的析构(销毁与回收)工作finalize是Object类中的一个方法并且是protected由于所有的类都继承了Object对象因此就都隐式的继承了改方法不过可以重写这个方法如果重写此方法最后一句必须写上superfinalize()语句因为finalize方法没有自动实现递归调用那我们在什么时候要重写它呢?当有一些不容易控制并且非常重要的资源时要放到finalize方法中例如一些I/O的操作数据的连接等等这些资源的释放对整个应用程序是非常关键的 我先让大家看一段代码 finalize方法最终是由JVM中的垃圾回收器调用的由于垃圾回收器调用finalize的时间是不确定或者不及时的调用时机对我们来说是不可控的因此我们可以在自己的类中声明一个destory()方法在这个方法中添加释放系统资源的处理代码但是还是建议你将对destroy()方法的调用放入当前类的finalize()方法体中因为这样做更保险更安全 静态变量我们知道类中的静态变量在程序运行期间其内存空间对所有该类的对象实例而言是共享的为了节省系统内存开销共享资源应该将一些变量声明为静态变量通过下面的例子你就会发现有什么不同 代码一 代码二 我想大家应该发现上面那两个类的区别了吧! 代码一会在内存中保存个weeks的副本而代码二则在内存中保存个weeks的副本然后共享该副本这样的话就不会造成内存的浪费 虽然静态的变量能节约大量的内存但是并不是所有的地方都适合用建议大家在下列条件都符合的情况下尽量用静态变量 ()变量所包含的对象体积较大占用内存较多 ()变量所包含的对象生命周期较长 ()变量所包含的对象数据稳定 ()该类的对象实例有对该变量所包含的对象的共享需求 如果变量不具备上述特点建议不要轻易使用静态变量以免弄巧成拙 最后再提一点内存的优化就是有关对象的重用比如对象池和数据库连接池等那样的话是很节约内存空间的不过在用的时候要考虑各个方面比如运行环境的内存资源的限制等为了防止对象池中的对象过多要记得清除 内存管理有许多技巧和方式其实内存管理有许多技巧和方式在这我给大家介绍一下 ()要尽早的释放无用对象的引用如果该对象不用了你可以把它设置为null但要注意如果该对象是某方法的返回值千万不要这样处理否则你从该方法中得到的返回值永远为空而且这种错误不易被发现因此这时很难及时抓住排除NullPointerException异常 ()尽量少用finalize函数因为它会加大GC的工作量因此尽量少用finalize方式回收资源 ()如果需要使用经常用到的图片可以使用soft应用类型(也就是转换为软引用类型)它可以尽可能将图片保存在内存中供程序调用而不引起OutOfMemory ()注意集合数据类型包括数组树图链表等数据结构这些数据结构对于GC来说回收更为复杂另外要注意那些全局变量静态变量这些对象往往容易引起悬挂对象造成内存浪费 ()尽量避免在类的默认构造器中创建初始化大量的对象防止在调用其子类的构造器时造成不必要的内存资源浪费 ()尽量避免强制系统做垃圾内存回收(通过显式调用方法Systemgc())增长系统做垃圾回收的最终时间降低系统性能 ()尽量避免显式申请数组空间当不得不显式申请数组空间时尽量准确的估计出其合理值以免造成不必要的系统内存开销 ()尽量在做远程方法调用(RMI)类应用开发时使用瞬间值变量除非远程调用端需要获取该瞬间值变量的值 ()尽量在合适的场景下使用对象池技术以提高系统的性能缩减系统内存开销但是要注意对象池的尺寸不易过大及时清除无效对象释放内存资源综合考虑应用运行环境的内存资源限制避免过高估计运行环境所提供内存资源的数量 虽然这些技巧提高不了多少性能但是在嵌入式开发或者要求性能比较高的系统中却很有用 |