虚拟主机网站空间,关键词排名提高,深圳网页制作页面排版,陕西营销型手机网站一、垃圾回收机制的意义Java语言中一个显著的特点就是引入了垃圾回收机制#xff0c;使c程序员最头疼的内存管理的问题迎刃而解#xff0c;它使得Java程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制#xff0c;Java中的对象不再有“作用域”的概念…一、垃圾回收机制的意义Java语言中一个显著的特点就是引入了垃圾回收机制使c程序员最头疼的内存管理的问题迎刃而解它使得Java程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制Java中的对象不再有“作用域”的概念只有对象的引用才有“作用域”。垃圾回收可以有效的防止内存泄露有效的使用空闲的内存。ps:内存泄露是指该内存空间使用完毕之后未回收在不涉及复杂数据结构的一般情况下Java 的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度我们有时也将其称为“对象游离”。二、垃圾回收机制中的算法Java语言规范没有明确地说明JVM使用哪种垃圾回收算法但是任何一种垃圾回收算法一般要做2件基本的事情(1)发现无用信息对象(2)回收被无用对象占用的内存空间使该空间可被程序再次使用。1.引用计数法(Reference Counting Collector)1.1算法分析引用计数是垃圾收集器中的早期策略。在这种方法中堆中每个对象实例都有一个引用计数。当一个对象被创建时且将该对象实例分配给一个变量该变量计数设置为1。当任何其它变量被赋值为这个对象的引用时计数加1(a b,则b引用的对象实例的计数器1)但当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时对象实例的引用计数器减1。任何引用计数器为0的对象实例可以被当作垃圾收集。当一个对象实例被垃圾收集时它引用的任何对象实例的引用计数器减1。1.2优缺点优点引用计数收集器可以很快的执行交织在程序运行中。对程序需要不被长时间打断的实时环境比较有利。缺点无法检测出循环引用。如父对象有一个对子对象的引用子对象反过来引用父对象。这样他们的引用计数永远不可能为0.1.3 引用计数算法无法解决循环引用问题例如public class Main {public static void main(String[] args) {MyObject object1 new MyObject();MyObject object2 new MyObject();object1.object object2;object2.object object1;object1 null;object2 null;}}最后面两句将object1和object2赋值为null也就是说object1和object2指向的对象已经不可能再被访问但是由于它们互相引用对方导致它们的引用计数器都不为0那么垃圾收集器就永远不会回收它们。2.tracing算法(Tracing Collector)或标记-清除算法(mark and sweep)2.1 根搜索算法根搜索算法是从离散数学中的图论引入的程序把所有的引用关系看作一张图从一个节点GC ROOT开始寻找对应的引用节点找到这个节点以后继续寻找这个节点的引用节点当所有的引用节点寻找完毕之后剩余的节点则被认为是没有被引用到的节点即无用的节点。java中可作为GC Root的对象有1、虚拟机栈中引用的对象(本地变量表)2、方法区中静态属性引用的对象3、方法区中常量引用的对象4、本地方法栈中引用的对象(Native对象)2.2 tracing算法的示意图2.3 标记-清除算法分析标记-清除算法采用从根集合进行扫描对存活的对象对象标记标记完毕后再扫描整个空间中未被标记的对象进行回收如上图所示。标记-清除算法不需要进行对象的移动并且仅对不存活的对象进行处理在存活对象比较多的情况下极为高效但由于标记-清除算法直接回收不存活的对象因此会造成内存碎片。3.compacting算法 或 标记-整理算法标记-整理算法采用标记-清除算法一样的方式进行对象的标记但在清除时不同在回收不存活的对象占用的空间后会将所有的存活对象往左端空闲空间移动并更新对应的指针。标记-整理算法是在标记-清除算法的基础上又进行了对象的移动因此成本更高但是却解决了内存碎片的问题。在基于Compacting算法的收集器的实现中一般增加句柄和句柄表。4.copying算法(Compacting Collector)该算法的提出是为了克服句柄的开销和解决堆碎片的垃圾回收。它开始时把堆分成一个对象面和多个空闲面程序从对象面为对象分配空间当对象满了基于copying算法的圾收集就从根集中扫描活动对象并将每个活动对象复制到空闲面(使得活动对象所占的内存之间没有空闲洞)这样空闲面变成了对象面原来的对象面变成了空闲面程序会在新的对象面中分配存。一种典型的基于coping算法的垃圾回收是stop-and-copy算法它将堆分成对象面和空闲区域面在对象面与空闲区域面的切换过程中程序暂停执行。5.generation算法(Generational Collector)分代的垃圾回收策略是基于这样一个事实不同的对象的生命周期是不一样的。因此不同生命周期的对象可以采取不同的回收算法以便提高回收效率。年轻代(Young Generation)1、所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。2、新生代内存按照8:1:1的比例分为一个eden区和两个survivor(survivor0,survivor1)区。一个Eden区两个 Survivor区(一般而言)。大部分对象在Eden区中生成。回收时先将eden区存活对象复制到一个survivor0区然后清空eden区当这个survivor0区也存放满了时则将eden区和survivor0区存活对象复制到另一个survivor1区然后清空eden和这个survivor0区此时survivor0区是空的然后将survivor0区和survivor1区交换即保持survivor1区为空 如此往复。3、当survivor1区不足以存放 eden和survivor0的存活对象时就将存活对象直接存放到老年代。若是老年代也满了就会触发一次Full GC也就是新生代、老年代都进行回收4、新生代发生的GC也叫做Minor GCMinorGC发生频率比较高(不一定等Eden区满了才触发)年老代(Old Generation)1、在年轻代中经历了N次垃圾回收后仍然存活的对象就会被放到年老代中。因此可以认为年老代中存放的都是一些生命周期较长的对象。2、内存比新生代也大很多(大概比例是1:2)当老年代内存满时触发Major GC即Full GCFull GC发生频率比较低老年代对象存活时间比较长存活率标记高。持久代(Permanent Generation)用于存放静态文件如Java类、方法等。持久代对垃圾回收没有显著影响但是有些应用可能动态生成或者调用一些class例如Hibernate 等在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。三.GC(垃圾收集器)新生代收集器使用的收集器Serial、PraNew、Parallel Scavenge老年代收集器使用的收集器Serial Old、Parallel Old、CMSSerial收集器(复制算法)新生代单线程收集器标记和清理都是单线程优点是简单高效。Serial Old收集器(标记-整理算法)老年代单线程收集器Serial收集器的老年代版本。ParNew收集器(停止-复制算法)新生代收集器可以认为是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现。Parallel Scavenge收集器(停止-复制算法)并行收集器追求高吞吐量高效利用CPU。吞吐量一般为99% 吞吐量 用户线程时间/(用户线程时间GC线程时间)。适合后台应用等对交互相应要求不高的场景。Parallel Old收集器(停止-复制算法)Parallel Scavenge收集器的老年代版本并行收集器吞吐量优先CMS(Concurrent Mark Sweep)收集器(标记-清理算法)高并发、低停顿追求最短GC回收停顿时间cpu占用比较高响应时间快停顿时间短多核cpu 追求高响应时间的选择四、GC的执行机制由于对象进行了分代处理因此垃圾回收区域、时间也不一样。GC有两种类型Scavenge GC和Full GC。Scavenge GC一般情况下当新对象生成并且在Eden申请空间失败时就会触发Scavenge GC对Eden区域进行GC清除非存活对象并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行不会影响到年老代。因为大部分对象都是从Eden区开始的同时Eden区不会分配的很大所以Eden区的GC会频繁进行。因而一般在这里需要使用速度快、效率高的算法使Eden去能尽快空闲出来。Full GC对整个堆进行整理包括Young、Tenured和Perm。Full GC因为需要对整个堆进行回收所以比Scavenge GC要慢因此应该尽可能减少Full GC的次数。在对JVM调优的过程中很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC1、年老代(Tenured)被写满2、持久代(Perm)被写满3、System.gc()被显示调用4、上一次GC之后Heap的各域分配策略动态变化五、Java有了GC同样会出现内存泄露问题1、静态集合类像HashMap、Vector等的使用最容易出现内存泄露这些静态变量的生命周期和应用程序一致所有的对象Object也不能被释放因为他们也将一直被Vector等应用着。Static Vector v new Vector();for (int i 1; i100; i){Object o new Object();v.add(o);o null;}在这个例子中代码栈中存在Vector对象的引用v和Object对象的引用o。在For循环中我们不断的生成新的对象然后将其添加到Vector对象中之后将o引用置空。问题是当o引用被置空后如果发生GC我们创建的Object对象是否能够被GC回收呢答案是否定的。因为GC在跟踪代码栈中的引用时会发现v引用而继续往下跟踪就会发现v引用指向的内存空间中又存在指向 Object对象的引用。也就是说尽管o引用已经被置空但是Object对象仍然存在其他的引用是可以被访问到的所以GC无法将其释放掉。如果在此循环之后Object对象对程序已经没有任何作用那么我们就认为此Java程序发生了内存泄漏。2、各种连接数据库连接网络连接IO连接等没有显示调用close关闭不被GC回收导致内存泄露。3、监听器的使用在释放对象的同时没有相应删除监听器的时候也可能导致内存泄露。