a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 129|回复: 1

[其他] JAVA技巧:对高性能JAVA代码之内存管理

[复制链接]
发表于 2012-8-4 12:28:23 | 显示全部楼层 |阅读模式
对高机能JAVA代码之内存打点& t8 [, n6 W$ t) K/ F' B* @
更甚者你写的代码,GC根柢就收受接管不了,直接系统挂失踪。GC是一段轨范,不是智能,他只收受接管他认为的垃圾,而不是收受接管你认为的垃圾。
! F* W+ H: X" C& D& Z: a' S0 f) u8 oGC垃圾收受接管:8 G+ b9 a' h& [: D7 x) d# b2 e
Grabage Collection相信学过JAVA的人都知道这个是什么意思。可是他是若何工作的呢?. S& P$ R# o' V
首先,JVM在打点内存的时辰对于变量的打点老是分新对象和老对象。新对象也就是开发者new出来的对象,可是因为生命周期短,那么他占用的内存并不是马上释放,而是被标识表记标帜为老对象,这个时辰该对象仍是要存在一段时刻。然后由JVM抉择他是否是垃圾对象,并进行收受接管。
& j5 Q: z, T9 I# O- d9 Y所以我们可以知道,垃圾内存并不是用完了马上就被释放,所以就会发生内存释放不实时的现象,从而降低了内存的使用。而当轨范浩荡的时辰。这种现象更为较着,而且GC的工作也是需要耗损资本的。所以,也就会发生内存华侈。
' H4 G! P. A; e$ {) }5 lJVM中的对象生命周期里谈内存收受接管:' W; d1 _) V# ~' t# @% T) |  A
对象的生命周期一般分为7个阶段:建树阶段,应用阶段,不成视阶段,不成达到阶段,可收集阶段,终结阶段,释放阶段。
  f& _% f0 M0 }' R- \/ q! N建树阶段:首先巨匠看一下,如下两段代码:
' [4 e4 S7 O5 Ktest1:4 w/ C' o3 V0 E  J" F+ k) {
for( int i=0; i《10000; i++)" S$ ]/ z! h- I' L' c$ p/ z
Object obj=new Object();
5 }2 N3 c0 t3 ?0 s+ ttest2:$ L: R; H: e0 }! E5 G2 J% X  w* K, R
Object obj=null;6 V6 e, V6 A7 I/ H
for( int i=0; i《10000; i++)
9 W4 V/ H. M& Z& e- K9 T5 ?obj=new Object();/ l6 W- C( M6 C$ i: @
这两段代码都是不异的功能,可是显然test2的机能要比test1机能要好,内存使用率要高,这是为什么呢?原因很简单,test1每次执行for轮回都要建树一个Object的姑且对象,可是这些姑且对象因为JVM的GC不能马上销毁,所以他们还要存在很长时刻,而test2则只是在内存中保留一份对象的引用,而不必建树大量新姑且变量,从而降低了内存的使用。2 v7 h* x' Y( x0 ~
此外不要对统一个对象初始化多次。例如:
& p2 }2 K, |( Lpublic class A{
3 X5 J& K% r8 g* zprivate Hashtable table = new Hashtable();/ c: W* K0 h" L! ~
public A(){ table = new Hashtable();7 q6 b! [6 ~7 y4 L# j
// 这里应该去失踪,因为table已经被初始化。/ p! d4 E& r- ^* v. Z
}# ?4 L' g( w6 y. s, w) f
% j3 d* @4 E& X5 Y8 |
( f# M! U* U+ X/ F6 A0 S* a" w2 P
这样就new了两个Hashtable,可是却只使用了一个。此吐矣闽则没有被引用。而被忽略失踪。华侈了内存。而且因为进行了两次new操作。也影响了代码的执行速度。- ?4 G' T( c4 Z8 ]; C# k
应用阶段:即该对象至少有一个引用在维护他。
6 A& F2 @# C7 L: T- M不成视阶段:即超出该变量的浸染域。这里有一个很好的做法,因为JVM在GC的时辰并不是马长进行收受接管,而是要判定对象是否被其他引用在维护。所以,这个时辰如不美观我们在使用完一个对象往后对其obj=null或者obj.doSomething()操作,将其标识表记标帜为空,可以辅佐JVM实时发现这个垃圾对象。
: m9 _& [# @- Y3 U' j7 ]- J" Z% M不成达到阶段:就是在JVM中找不到对该对象的直接或者借居的引用。8 w( L/ G2 I1 K

) R  m" }- x  ?# H& w, Y可收集阶段,终结阶段,释放阶段:此为收受接管器发现该对象不成达到,finalize体例已经被执行,或者对象空间已被重用的时辰。
回复

使用道具 举报

 楼主| 发表于 2012-8-4 12:28:24 | 显示全部楼层

JAVA技巧:对高性能JAVA代码之内存管理

</p>JAVA的析构体例:
, q) [! B; S: ^可能不会有人相信,JAVA有析构函数? 是的,有。因为JAVA所有类都担任至Object类,而finalize就是Object类的一个体例,这个体例在JAVA中就是近似于C++析构函数。一般来说可以经由过程重载finalize体例的形式才释放类中对象。如:
, |% q. L$ t9 K: u! k' x) L7 lpublic class A{
  Y& \6 _  T9 |4 C. spublic Object a;
: \1 Y4 ?, |4 f) v" a9 k3 a7 mpublic A(){ a = new Object ;}
3 q! K4 L6 [8 Y7 [$ l0 Z' T# kprotected void finalize() throws java.lang.Throwable{/ F% j  ]/ d( g8 N* \1 `
a = null; // 标识表记标帜为空,释放对象
" G" Y1 _: N( Y3 @- Csuper.finalize(); // 递归挪用超类中的finalize体例。
5 w) \0 L0 W7 R8 p( `}
/ R/ z, S( P8 B) F}
& v, ~2 n- i2 V当然,什么时辰该体例被挪用是由JVM来抉择的。..。..。..。..。..。..。..。." H7 T6 b# ?; O
一般来说,我们需要建树一个destory的体例来显式的挪用该体例。然后在finalize也对该体例进行挪用,实现双保险的做法。; T' @+ Z0 h: h$ n$ x
因为对象的建树是递归式的,也就是先挪用超级类的机关,然后依次向下递归挪用机关函数,所以应该避免在类的机关函数中初始化变量,这样可以避免不需要的建树对象造成不需要的内存耗损。当然这里也就看出来接口的优势。% l0 W* v1 c. i
数组的建树:3 @& F  e: ~4 ~
因为数组需要给定一个长度,所以在不确定数据数目的时辰经常会建树过大,或过小的数组的现象。造成不需要的内存华侈,所以可以经由过程软引用的体例来告诉JVM实时收受接管该内存。(软引用,具体查资料)。
- c/ Y: n  X) N7 y1 J例如:7 I" n! r) k# I% w, v5 N
Object obj = new char[10000000000000000];' `; M- ]: x& \0 ?# g% G, S" a
SoftReference ref = new SoftReference(obj);
+ Q* {! z. ^" V  ^. O4 n2 w共享静态存储空间:- L1 ?5 r- n) U  y6 s
我们都知道静态变量在轨范运行时代其内存是共享的,是以有时辰为了节约内存工件,将一些变量声明为静态变量确实可以起到节约内存空间的浸染。可是因为静态变量生命周期很长,不易被系统收受接管,所以使用静态变量要合理,不能盲目的使用。以免适得其反。8 u5 Z1 A* a1 C, ?! p
是以建议不才面情形下使用:* E/ B2 M/ b# h1 H! g* e
1,变量所包含的对象体积较大,占用内存过多。7 d0 i; R; g- \; V! g! a  P* a$ L
2,变量所包含对象生命周期较长。
4 G- l8 I* I" H. U5 O" N0 @3 H0 }$ j3,变量所包含数据不变。0 O, w; p% r/ p# M0 K! A
4,该类的对象实例有对该变量所包含的对象的共先缤。(也就是说是否需要作为全局变量)。/ \- }! _* X. o( K. E
对象重用与GC:
3 d2 l, E- P5 E( O0 R5 O: K有的时辰,如数据库操作对象,一般情形下我们都需要在各个分歧模块间使用,所以这样的对象需要进行重用以提高机能。也有用的避免了一再建树对象引起的机能下降。1 u0 \9 p" ]0 \' q0 ^0 t
一般来说对象池是一个不错的注重。如下:' m4 Q+ B7 G9 N# {+ C3 J
public abstarct class ObjectPool{
  T: K. H, k9 Q8 M8 |5 Mprivate Hashtable locked,unlocked;
+ T8 l5 m- V( J: a: ]private long expirationTime;- {1 K( T6 b% y$ I% Y+ I/ }  i# U
abstract Object create();$ }- z( x3 c. o
abstract void expire( Object o);4 k! P7 n/ D: ^) S3 j
abstract void validate( Object o);8 `  i8 D; U8 u, q
synchronized Object getObject(){。..};4 J  w9 k3 o& d! G' |. P1 G6 g
synchronized void freeObject(Object o){。..};
% a' b; l. q5 x; {8 K+ D0 u6 g# [. x这样我们就完成了一个对象池,我们可以将经由过程对应的体例来存取删除所需对象。来维护这快内存提高内存重用。
; c) c5 p4 t/ v. K( k当然也可以经由过程挪用System.gc()强制系统进行垃圾收受接管操作。当然这样的价钱是需要耗损一些cpu资本。
2 f" f: p" L! q; i! Y) j不要提前建树对象:
  O4 }. w' w. n尽量在需要的时辰建树对象,一再的分配,机关对象可能会因为垃圾收受接管做额外的工作降低机能。
; L. d  I- t0 H1 w7 |/ q; EJVM内存参数调优:+ [* K& Y8 g: d( @/ u
强制内存收受接管对于系统自动的内存收受接管机制会发生负面影响,会加大系统自动收受接管的措置时刻,所以应该尽量避免显式使用System.gc(),* @. K! M' G  K( v
JVM的设置可以提高系统的机能。例如:- ^2 x$ m5 R4 f% Q1 K7 _; q  k3 Q0 Q
java -XX:NewSize=128m -XX:MaxNewSize=128m -XX:SurvivorRatio=8 -Xms512m -Xmx512m
3 X4 M' U: T) ?! o* g( u$ Y7 |具体可以查看java辅佐文档。我们首要介绍轨范设计方面的机能提高。* f" G- v' z- D' j& @
JAVA轨范设计中有关内存打点的其他经验:
0 s' |# k5 k- t7 X/ T按照JVM内存打点的工作事理,可以经由过程一些技巧和体例让JVM做GC措置时加倍有用。,从而提高内存使用和缩短GC的执行时刻。; Q3 @2 S* V- X* l* S6 G, z
1,尽早释放无用对象的引用。即在不使用对象的引用后设置为空,可以加速GC的工作。(当然如不美观是返回值。..。.)+ c& Z$ M; l0 h( r
2,尽量少用finalize函数,此函数是JAVA给轨范员供给的一个释放对象或资本的机缘,可是却会加大GC工作量。
3 F$ B: A7 U, ~8 d$ _4 |  L3 P3,如不美观需要使用到图片,可以使用soft应用类型,它可以尽可能将图片读入内存而不引起OutOfMemory.% G  e' f0 l: I) p+ E" _
4,注重集结数据类型的数据结构,往往数据结构越复杂,GC工作量更大,措置更复杂。( [  j- W" D7 G% ?. Y: l+ F
5,尽量避免在默认机关器(机关函数)中建树,初始化大量的对象。4 _. L7 V. K8 N- S( P
6,尽量避免强制系统做垃圾收受接管。会增添系统做垃圾收受接管的最终侍旧说低机能。
8 @* K! Y, @1 g; x- f+ B7 \6 {7,尽量避免显式申请数组,如不美观不得不申请数组的话,要尽量切确估算数组巨细。2 V/ v. ]# f. J4 u$ i+ F+ W
8,如不美观在做远程体例挪用。要尽量削减传递的对象巨细。或者使用瞬间值避免不需要数据的传递。
+ u1 e8 p. n( Y: q9 p3 g9,尽量在合适的情形下使用对象池来提高系统机能削减内存开销,当然,对象池不能过于复杂,会适得其反.
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-6-16 22:31 , Processed in 1.400119 second(s), 24 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表