1
、
Java
的内存管理就是对象的分配和释放问题。
在
Java
中,程序员需要通过关键字
new
为每个对象申请内存空间
(
基本类型除
外
)
,所有的对象都在堆
(Heap)
中分配空间。
对象的释放是由
GC
决定和执行的。
在
Java
中,
内存的分配是由程序完成的,
而内存的释放是有
GC
完成的,
这种收
支两条线的方法简化了程序员的工作。但也加重了
JVM
的工作。这也是
Java
程
序运行速度较慢的原因之一。
GC
释放空间方法:
监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等。当该对
象不再被引用时,释放对象。
2
、内存管理结构
Java
使用有向图的方式进行内存管理,对于程序的每一个时刻,我们都有一个
有向图表示
JVM
的内存分配情况。
将对象考虑为有向图的顶点,
将引用关系考虑为图的有向边,
有向边从引用者指
向被引对象。
另外,
每个线程对象可以作为一个图的起始顶点,
例如大多程序从
main
进程开始执行,那么该图就是以
main
进程顶点开始的一棵根树。在这个有
向图中,根顶点可达的对象都是有效对象,
GC
将不回收这些对象。如果某个对
象
(
连通子图
)
与这个根顶点不可达
(
注意,该图为有向图
)
,那么我们认为这个
(
这些
)
对象不再被引用,可以被
GC
回收。
3
、使用有向图方式管理内存的优缺点
Java
使用有向图的方式进行内存管理,可以消除引用循环的问题,例如有三个
对象,相互引用,只要它们和根进程不可达的,那么
GC
也是可以回收它们的。
这种方式的优点是管理内存的精度很高,但是效率较低。
另外一种常用的内存管理技术是使用计数器,
例如
COM
模型采用计数器方式管理
构件,
它与有向图相比,
精度行低
(
很难处理循环引用的问题
)
,
但执行效率很高。
★ Java
的内存泄露
Java
虽然由
GC
来回收内存,但也是存在泄露问题的,只是比
C++
小一点。
1
、与
C++
的比较
c++
所有对象的分配和回收都需要由用户来管理。
即需要管理点,
也需要管理边。
若存在不可达的点,
无法在回收分配给那个点的内存,
导致内存泄露。
存在无用
的对象引用,自然也会导致内存泄露。
Java
由
GC
来管理内存回收,
GC
将回收不可达的对象占用的内存空间。
所以,
Java
需要考虑的内存泄露问题主要是那些被引用但无用的对象——即指要管理边就
可以。
被引用但无用的对象,
程序引用了该对象,
但后续不会再使用它。
它占用
的内存空间就浪费了。
如果存在对象的引用,这个对象就被定义为“活动的”,同时不会被释放。
2
、
Java
内存泄露处理
处理
Java
的内存泄露问题:确认该对象不再会被使用。
典型的做法——
把对象数据成员设为
null
从集合中移除该对象
注意,
当局部变量不需要时,
不需明显的设为
null
,
因为一个方法执行完毕时,
这些引用会自动被清理。
例子:
List myList=new ArrayList();
for (int i=1;i<100; i++)
{
Object o=new Object();
myList.add(o);
o=null;
}
//
此时,所有的
Object
对象都没有被释放,因为变量
myList
引用这些对象。
当
myList
后来不再用到,将之设为
null
,释放所有它引用的对象。之后
GC
便
会回收这些对象占用的内存。
★ 对
GC
操作
对
GC
的操作并不一定能达到管理内存的效果。
GC
对于程序员来说基本是透明的,不可见的。我们只有几个函数可以访问
GC
,
例如运行
GC
的函数
System.gc()
,
System.
。
但是根据
Java
语言规范定义,
System.gc()
函数不保证
JVM
的垃圾收集器一定
会执行。因为,不同的
JVM
实现者可能使用不同的算法管理
GC
。通常,
GC
的线
程的优先级别较低。
JVM
调用
GC
的策略有很多种,
有的是内存使用到达一定程度时,
GC
才开始工作,
也有定时执行的,有的是平缓执行
GC
,有的是中断式执行
GC
。但通常来说,我
们不需要关心这些。除非在一些特定的场合,
GC
的执行影响应用程序的性能,
例如对于基于
Web
的实时系统,如网络游戏等,用户不希望
GC
突然中断应用程
序执行而进行垃圾回收,
那么我们需要调整
GC
的参数,
让
GC
能够通过平缓的方
式释放内存,例如将垃圾回收分解为一系列的小步骤执行,
Sun
提供的
HotSpot
JVM
就支持这一特性。
★ 内存泄露检测
市场上已有几种专业检查
Java
内存泄漏的工具,
它们的基本工作原理大同小异,
都是通过监测
Java
程序运行时,所有对象的申请、释放等动作,将内存管理的
所有信息进行统计、
分析、
可视化。
开发人员将根据这些信息判断程序是否有内
存泄漏问题。
这些工具包括
Optimizeit
Profiler
,
JProbe
Profiler
,
JinSight
,
Rational
公司的
Purify
等。
在运行过程中,
我们可以随时观察内存的使用情况,
通过这种方式,
我们可以很
快找到那些长期不被释放,
并且不再使用的对象。
我们通过检查这些对象的生存
周期,确认其是否为内存泄露。
★ 软引用
特点:只有当内存不够的时候才回收这类内存,同时又保证在
Java
抛出
OutOfMemory
异常之前,被设置为
null
。
保证最大限度的使用内存而不引起
OutOfMemory
异常。
在某些时候对软引用的使用会降低应用的运行效率与性能,
例如:
应用软引用的
对象的初始化过程较为耗时,或者对象的状态在程序的运行过程中发生了变化,
都会给重新创建对象与初始化对象带来不同程度的麻烦。
用途:
可以用于实现一些常用资源的缓存,实现
Cache
的功能
处理一些占用内存大而且声明周期较长,
但使用并不频繁的对象时应尽量应用该
技术
★ java
程序设计中有关内存管理的经验
1
.最基本的建议是尽早释放无用对象的引用。如:
...
A a = new A();
//
应用
a
对象
a = null; //
当使用对象
a
之后主动将其设置为空
„.
注:如果
a
是方法的返回值,不要做这样的处理,否则你从该方法中得到的返
回值永远为空,而且这种错误不易被发现、排除
2
.尽量少用
finalize
函数。它会加大
GC
的工作量。
3
.
如果需要使用经常用到的图片,
可以使用
soft
应用类型。
它尽可能把图片保
存在内存中
4
.注意集合数据类型,包括数组、树、图、链表等数据结构,这些数据结构对
GC
来说,回收更为复杂。
5
.尽量避免在类的默认构造器中创建、初始化大量的对象,防止在调用其自类
的构造器时造成不必要的内存资源浪费
6
.尽量避免强制系统做垃圾内存的回收,增长系统做垃圾回收的最终时间
7
.尽量避免显式申请数组空间
8
.尽量做远程方法调用类应用开发时使用瞬间值变量,除非远程调用端需要获
取该瞬间值变量的值。
9
.尽量在合适的场景下使用对象池技术以提高系统性能。