Java垃圾回收机制

java的垃圾回收机制是JVM虚拟机提供的能力,用于在空闲时间以不定时的方式动态回收无任何引用的对象占据的内存空间


如何确定某个对象是垃圾

在java中是通过引用来和对象进行关联的,要用对象就得引用,引用是通过引用计算来确定做这个对象是否可以被回收
如果这个对象没有引用,那就这个对象就是无用对象,就会被垃圾回收器回收。


如何计算那个对象无用



一共有四种常用的垃圾回收算法


1.Mark-sweep(标记-清除)算法


Mark-sweep分为两个阶段,标记阶段和清除阶段,标记阶段就是标记所有会被回收的对象
,清除阶段就是清除标记完成的无用对象,释放内存


这中算法有个坏处就是,回收完垃圾对象会产生内存碎片,这样会导致大对象无法分配足够
大的内存空间,这样会导致再次标记删除


什么是内存碎片


存入的对象都会有一个先后顺序


例子
我们有1,2,3,4,5,6个仓库,现在来了10车货分别把1,2,3,4,5仓库都装满了
这个时候客户需要4号仓库的货物,这样一来我还实际还剩下4,6号仓库,但是现在又来一批货物
需要连续存入,这样一来我们没有连续的仓库,导致对象存不进来这就导致内存碎片




2.Copying(复制)算法

为了弥补标记-清除算法的缺陷,复制算法会把内存分成两块,我们存入的对象只占用一块,当这块内存
被用完时,复制算法会把有用的对象复制到另一块内存里,然后把使用过的全部清除,这样就不会
出现碎片问题

这个算法也有不足,它会大大的减少内存的使用,会把内存减半,如果存货的对象过多
会降低复制的效率

3.Mark-Compact(标记-整理)算法

为了解决复制算法的缺陷,充分利用内存空间,这个算法跟标记-清除算法差不多,但是它标记过后
不时被清除,而是把有用的对象整理出来,它会把有用的对象向一端移动,然后删除另一端的无用对象


4.Generational Collection(分代收集)算法


分代收集算法时目前大部分JVM的垃圾回收器采用的算法,它的思想时根据对象的生命周期进行划分
划分不同的区域,一般分为老年代(Tenured Generatuin)和新生代(Young Generation)
老年代的特点是每次回收就会回收少量的对象,新生代的特点是每次回收都会有大量的对象被回收
对象被回收是分不同代进行不同的算法,老年代采用的是(标记-整理)的算法,新生代采用的是(复制)
算法,因为新生代回收的对象比较多,复制的操作比较少,从而给新生代划分的区域比较大,Eolen空间
和两块较小的Survivo空间,每次回收会用到Eolen空间和一块Survivo空间,把有用的对象复制到另一个Survivo
空间,然后清理掉Eolen和Survivo空间,

注:在堆中还有一个永久代(Permanet-Generation),用来储存Class类和常量方法等,采取的是废弃的
常量和无用的类
垃圾回收器


垃圾回收器是内存的具体实现,HotSpot(JDK 7)提供的几种垃圾回收器


1.ParNew收集器


它是采用了多线程进行收集


2.Serial/Serialold 收集齐


它是最基本最古老的收集齐,它是单线程的收集齐,它在收集的时候必须把所有用户线程全部暂停
Serial是针对新生代的收集齐,采用了复制算法
Serialold是针对老年代的收集齐。采用了标记整理算法
优点:是简单高效  缺点:回给用户带来停顿


3.Parallel Scavenge 收集齐


它是针对新生代的多线程并行的收集器,它在收集的时候不需要暂停,采用了复制算法
它与前两个收集器不同,它主要是可以控制吞吐量
4.Parallel Old 是Serialold的老年代(并行收集齐)使用多线程的标记整理算法


5.CMS 收集齐

它是获取最短停顿时间为目标的收集齐,(是并发收集齐)采用了标记-清除算法


6.GI 收集齐


GI是目前发展最前沿的成果,它是面向服务端的应用收集器,它能充分的利用多CPU和多核环境,
因此他是(并行和并发收集齐),它能建立可预测停顿时间的模型,内存的分配往大的方向讲
就是分配在堆上,对象主要会分配在新生代的Eden Space和From Space,少数会分配在老年代
如果新生代内存不足就会发生一次GC,发生一次GC之后,如果新生代的两个内存空间不足,
GC的过程中两个空间会把存货的对象放入 To Space 如果装不下就会存到老年代
在进行GC之后就会使用 Eden Space和To Space了

大对象就直接存储在老年代,大对象就是指需要大量连续存储的空间,也就是大数组


byte[] byte = new byte[1024*1024*4];


大牛勿喷

阅读更多

java垃圾回收机制小结

07-04

[url=http://bbs.189works.com/][color=#000000]Java[/color][/url]垃圾回收机制小结rnrn一.谁在做Garbage Collection?rn  一种流行的说法:在C++里,是系统在做垃圾回收;而在Java里,是Java自身在做。rn  在C++里,释放内存是手动处理的,要用delete运算符来释放分配的内存。这是流行的说法。确切地说,是应用认为不需要某实体时,就需用delete 告诉系统,可以回收这块空间了。这个要求,对编码者来说,是件很麻烦、很难做到的事。随便上哪个BBS,在C/C++版块里总是有一大堆关于内存泄漏的话题。rn  Java采用一种不同的,很方便的方法:Garbage Collection。垃圾回收机制放在JVM里。JVM完全负责垃圾回收事宜,应用只在需要时申请空间,而在抛弃对象时不必关心空间回收问题。rn  二.对象在啥时被丢弃?rn  在C++里,当对象离开其作用域时,该对象即被应用抛弃。rn  在Java里,对象的生命期不再与其作用域有关,而仅仅与引用有关。rn  Java的垃圾回收机制一般包含近十种算法。对这些算法中的多数,我们不必予以关心。只有其中最简单的一个:引用计数法,与编码有关。rn  一个对象,可以有一个或多个引用变量指向它。当一个对象不再有任何一个引用变量指向它时,这个对象就被应用抛弃了。或者说,这个对象可以被垃圾回收机制回收了。这就是说,当不存在对某对象的任何引用时,就意味着,应用告诉JVM:我不要这个对象,你可以回收了。rn  JVM的垃圾回收机制对堆空间做实时检测。当发现某对象的引用计数为0时,就将该对象列入待回收列表中。但是,并不是马上予以销毁。rn  三.丢弃就被回收?rn  该对象被认定为没有存在的必要了,那么它所占用的内存就可以被释放。被回收的内存可以用于后续的再分配。rn  但是,并不是对象被抛弃后当即被回收的。JVM进程做空间回收有较大的系统开销。如果每当某应用进程丢弃一个对象,就立即回收它的空间,势必会使整个系统的运转效率非常低下。前面说过,JVM的垃圾回收机制有多个算法。除了引用计数法是用来判断对象是否已被抛弃外,其它算法是用来确定何时及如何做回收。 JVM的垃圾回收机制要在时间和空间之间做个平衡。rn  因此,为了提高系统效率,垃圾回收器通常只在满足两个条件时才运行:即有对象要回收且系统需要回收。切记垃圾回收要占用时间,因此,Java运行时系统只在需要的时候才使用它。因此你无法知道垃圾回收发生的精确时间。rn  四.没有引用变量指向的对象有用吗?rn  前面说了,没挂上引用变量的对象是被应用丢弃的,这意味着,它在堆空间里是个垃圾,随时可能被JVM回收。不过,这里有个不是例外的例外。对于一次性使用的对象(有些书称之为临时对象),可以不用引用变量指向它。举个最简单也最常见的例子:System.out.println(“I am Java!”);就是创建了一个字符串对象后,直接传递给println()方法。rn  五.应用能干预垃圾回收吗?rn  许多人对Java的垃圾回收不放心,希望在应用代码里控制JVM的垃圾回收运作。这是不可能的事。对垃圾回收机制来说,应用只有两个途径发消息给JVM。第一个前面已经说了,就是将指向某对象的所有引用变量全部移走。这就相当于向JVM发了一个消息:这个对象不要了。第二个是调用库方法 System.gc(),多数书里说调用它让Java做垃圾回收。rn  第一个是一个告知,而调用System.gc()也仅仅是一个请求。JVM接受这个消息后,并不是立即做垃圾回收,而只是对几个垃圾回收算法做了加权,使垃圾回收操作容易发生,或提早发生,或回收较多而已。rn  希望JVM及时回收垃圾,是一种需求。其实,还有相反的一种需要:在某段时间内最好不要回收垃圾。要求运行速度最快的实时系统,特别是嵌入式系统,往往希望如此。rn  Java的垃圾回收机制是为所有Java应用进程服务的,而不是为某个特定的进程服务的。因此,任何一个进程都不能命令垃圾回收机制做什么、怎么做或做多少。rn  六.对象被回收时要做的事rn  一个对象在运行时,可能会有一些东西与其关连。因此,当对象即将被销毁时,有时需要做一些善后工作。可以把这些操作写在finalize()方法(常称之为终止器)里。rn  protected void finalize()rn  rn  // finalization code herern  rn  这个终止器的用途类似于C++里的析构函数,而且都是自动调用的。但是,两者的调用时机不一样,使两者的表现行为有重大区别。C++的析构函数总是当对象离开作用域时被调用。这就是说,C++析构函数的调用时机是确定的,且是可被应用判知的。但是,Java终止器却是在对象被销毁时调用。一旦垃圾收集器准备好释放无用对象占用的存储空间,它首先调用那些对象的finalize()方法,然后才真正回收对象的内存。由上所知,被丢弃的对象何时被销毁,应用是无法获知的。而且,对于大多数场合,被丢弃对象在应用终止后仍未销毁。rn  在编码时,考虑到这一点。譬如,某对象在运作时打开了某个文件,在对象被丢弃时不关闭它,而是把文件关闭语句写在终止器里。这样做对文件操作会造成问题。如果文件是独占打开的,则其它对象将无法访问这个文件。如果文件是共享打开的,则另一访问该文件的对象直至应用终结仍不能读到被丢弃对象写入该文件的新内容。rn  至少对于文件操作,编码者应认清Java终止器与C++析构函数之间的差异。rn  那么,当应用终止,会不会执行应用中的所有finalize()呢?据Bruce Eckel在Thinking in Java里的观点:“到程序结束的时候,并非所有收尾模块都会得到调用”。这还仅仅是指应用正常终止的场合,非正常终止呢?因此,哪些收尾操作可以放在 finalize()里,是需要酌酎的。rn  七.Thinking ing java 一书中也对垃圾回收做了一些小结rn  java垃圾回收,主要是靠一个低优先级的进程负责回收,注意,不是后台的进程rn  他的优点是边回收,边调整堆使其紧凑rn  主要有以下几种算法:rn  1.引用计数rn  该算法在java虚拟机没被使用过,主要是循环引用问题,因为计数并不记录谁指向他,无法发现这些交互自引用对象。rn  怎么计数?rn  当引用连接到对象时,对象计数加1rn  当引用离开作用域或被置为null时减1rn  怎么回收?rn  遍历对象列表,计数为0就释放rn  有什么问题?rn  循环引用问题。rn  2.标记算法rn  标记算法的思想是从堆栈和静态存储区的对象开始,遍历所有引用,标记活得对象rn  对于标记后有两种处理方式rn  (1)停止-复制rn  所谓停止,就是停止在运行的程序,进行垃圾回收rn  所谓复制,就是将活得对象复制到另外一个堆上,以使内存更紧凑rn  优点在于,当大块内存释放时,有利于整个内存的重分配rn  有什么问题?rn  一、停止,干扰程序的正常运行,二,复制,明显耗费大量时间,三,如果程序比较稳定,垃圾比较少,那么每次重新复制量是非常大的,非常不合算rn  什么时候启动停止-复制?rn  内存数量较低时,具体多低我也不知道rn  (2)清除rn  也称标记-清除算法rn  也就是将标记为非活得对象释放,也必须暂停程序运行rn  优点就是在程序比较稳定,垃圾比较少的时候,速度比较快rn  有什么问题?rn  很显然停止程序运行是一个问题,只清除也会造成很对内存碎片。rn  为什么这2个算法都要暂停程序运行?rn  这是因为,如果不暂停,刚才的标记会被运行的程序弄乱,rn  (3)分代收集rn  分代收集是利用程序有大量临时对象的特点,对象每被引用一次,代数就增加,代数小的小型对象会被回收整理,大对象只会代数增加,不会被整理。rn  优点在于对于处理大量临时的变量很有帮助rn  (4)自适应rn  jvm会监测垃圾回收的效率,在(1),(2)算法之间切换。rn  3.增量收集,rn  增量回收的主要算法还是分代(Young Objects 回收)与Train算法(Mature Object回收),所谓增量回收的关键问题是如何实现有序的增量回收而不会导致混乱(引用及其的增加与减少),分代可以逐代回收,Train算法可以逐个车厢回收,这样每次一代或每次一厢可以实现短停顿回收。

没有更多推荐了,返回首页