JAVA垃圾回收个人总结_第1页
JAVA垃圾回收个人总结_第2页
JAVA垃圾回收个人总结_第3页
JAVA垃圾回收个人总结_第4页
JAVA垃圾回收个人总结_第5页
已阅读5页,还剩8页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、GC: (Garbage Collection,垃圾收集,垃圾回收)程序计数器,java虚拟机栈,本地方法三个区随线程而生,随线程而灭。 其中栈中的栈帧随方法的进入和退出而有条不紊的执行着出栈入栈操作。垃圾回收针对的区域: Java堆和方法区(主要还是堆)很多人认为方法区(或者HotSpot虚拟机中的永久代)是没有垃圾收集的,Java虚拟机规范中确实说过可以不要求虚拟机在方法区实现垃圾收集,而且在 方法区进行垃圾收集的“性价比”一般比较低:在堆中,尤其是在新生代中,常规应用进行一次垃圾收集一般可以回收70%95%的空间,而永久代的垃圾收集 效率远低于此。无用的类”:该类所有的实例都已经被回收,

2、也就是Java堆中不存在该类的任何实例。加载该类的ClassLoader已经被回收。该类对应的java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。虚拟机可以对满足上述3个条件的无用类进行回收,这里说的仅仅是“可以”,而不是和对象一样,不使用了就必然会回收。垃圾收集器:主要处理的问题- 1)哪些对象需要回收? 2)何时回收这些对象? 3)如何回收这些对象?垃圾回收算法:1.引用计数器法方法描述:给对象中添加一个计数器,每当有一个地方应用它时,计数器值就加1;每当引用失效时,计数器值就减1.任何计数器为0的对象就是不可能在使用的对象。优点:实现简单,判定

3、效率高。缺点:无法解决对象之间循环引用的问题。(比如A引用了B,B也引用了A,除此之外这两个对象再无其他引用。此时这两个对象已经无法在被访问了,但由于计数器不为0,无法回收他们。)注:主流虚拟机不采用引用计数器法管理内存。采用可达性分析算法(根搜索算法):判断对象是否存活。即:通过一系列称为“GC Roots”的对象作为起点,从这些节点开始向下搜索,搜索所走过的路径称为应用链,当一个对象到GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。在Java语言里,可作为GC Roots的对象包括下面几种:虚拟机栈(栈帧中的本地变量表)

4、中的引用的对象。方法区中的类静态属性引用的对象。方法区中的常量引用的对象。本地方法栈中JNI(即一般说的Native方法)的引用的对象。2.标记清除算法 方法描述: 算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象(一种可行实现:在标记阶段,首先通过根节点,标记所有从根节点可达的对象),在标记完成后统一回收掉所有被标记的对象.优点:缺点:一个是效率问题,标记和清除过程的效率都不高;另外一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致,当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。 回收后的空间是

5、不连续的。(在对象的堆空间分配过程中,尤其是大对象的内存分配,不连续空的工作效率要低于连续的空间)3.复制算法/改进的复制算法 -现在的商业虚拟机都采用这种收集算法来回收新生代复制算法:方法描述:它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存 空间一次清理掉。这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简 单,运行高效。缺点: 只是这种算法的代价是将内存缩小为原来的一半,未免太高了一点。改进的复制算法: 方法描述:新

6、生代中的对象绝大部分是朝生夕死的,并不需要按照11的比例来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中的一块 Survivor。当回收时,将Eden和Survivor中还存活着的对象一次性地拷贝到另外一块Survivor空间上,最后清理掉Eden和刚才用 过的Survivor的空间。HotSpot虚拟机默认Eden和Survivor的大小比例是81,也就是每次新生代中可用内存空间为整个新生代容量 的90%(80%+10%),只有10%的内存是会被“浪费”的。当然,98%的对象可回收只是一般场景下的数据,我们没有办法保证每次回收都只有

7、不多于 10%的对象存活,当Survivor空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保(Handle Promotion) 内存的分配担保也一样,如果另外一块Survivor空间没有足够的空间存放上一次新生代收集下来的存活对象,这些对象将直接通过分配担保机制进入老年代。4.标记压缩算法(标记整理算法)复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。方法描述:标记过程仍然与“标记-清除”算法一样,但后

8、续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存5.增量算法方法描述:如果一次性将所有垃圾进行处理,需要造成系统长时间的停顿,那么就可以让垃圾收集线程与应用程序线程交替执行优点:减少系统停顿时间缺点: 线程切换,和上下文转换的消耗,会使得垃圾回收的总体成本上升,造成系统吞吐量下降。6.分代方法描述:根据对象的存活周期的不同将内存划分为几块一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量 存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以

9、完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标 记-清理”或“标记-整理”算法来进行回收。垃圾收集器:1.新生代串行收集器(Serial 收集器)-采用:复制算法描述:单线程收集器.垃圾收集时,必须暂停其他所有线程工作,直到收集结束.2.老年代串行收集器(Serial Old收集器)-采用:标记-整理算法描述: 同上3.并行收集器(ParNew 收集器) -采用:复制算法描述; 多条线程进行垃圾收集,必须暂停其他所有线程工作,直到收集结束.4.新生代并行回收器(Parallel Scavenge 收集器)-描述:并行多线程收集,目的:达到可控制的吞吐量(吞吐

10、量:运行用户代码时间/(运行用户代码时间+垃圾收集时间))5.老生代并行回收器(Parallel Old 收集器)-采用:标记-整理算法 6.CMS收集器(老生代)-采用:标记清除算法描述:获取最短回收停顿时间为目的的收集器。(并发的收集器:即用户线程和回收线程可同时运行) 初始标记(标记gc roots 直接关联的对象)-à并发标记(标记gc roots可达对象) -à 重新标记 (修正并发标记时,产生的一些变动)-à并发清除(清理垃圾)Cms缺点:cms收集器对cpu特别敏感:当cpu本身负载很大时,还要分出cpu给gc ,虚拟机提供了:增量并发收集器(交替运

11、行,尽量减少gc线程独占线程的时间)cms无法处理浮动垃圾:在GC收集器,收集垃圾的同时,用户线程还要产生垃圾,所以清理垃圾时,要预留一部分空间,提供并发收集时的程序运作使用。(如果不这样,可能 导致采用老年代串行收集器full gc)cms运行时会产生大量碎片:大对象无法分配,不得不提前触发一次Full gc(内部:会在顶不住要进行复制前,开启内存的合并整理过程。 也可以设置:-XX:CMSFullGCsBeforeCompaction执行多少次不压缩的full gc 后跟着来一次带压缩的。) 7.G1收集器(都可以)-采用:整体来看:标记-整理 局部来看:复制算法 不产生内存碎片。把jav

12、a堆划分为,多个大小相等的区域,虽然还保留了,新生代和老年代的概念,但是他们已经不再是物理隔离的了,他们都是一部分区域的集合。优先回收代价最大的区域。初始标记(标记gc roots 直接关联的对象)-à并发标记 (标记gc roots可达对象) -à 最终标记(修正并发标记时,产生的一些变动)-à筛选回收(并发的)(对各个区域的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划。)G1优点:1.并行与并发: 2.分代收集: 3.空间整合:整体上基于标记-整理算法,不产生碎片 4.可预测的停顿: 新生代 GC(Minor GC):指发生在新生代的垃圾

13、收集动作,因为 Java 对象大多都具 备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。老年代 GC(Major GC / Full GC):指发生在老年代的 GC,出现了 Major GC,经常会伴随至少一次的 Minor GC(但非绝对的,在 ParallelScavenge 收集器的收集策略里就有直接进行 Major GC 的策略选择过程) 。MajorGC 的速度一般会比 Minor GC 慢 10倍以上。虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被 Survivor 容纳的

14、话,将被移动到 Survivor 空间中,并将对象年龄设为 1。对象在 Survivor 区中每熬过一次 Minor GC,年龄就增加 1 岁,当它的年龄增加到一定程度(默认为 15 岁)时,就会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置。Java内存泄露:内存泄露是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成的内存空间的浪费称为内存泄露。Vector v = new Vector(10);for (int i = 1; i<100; i+)Object o = new Objec

15、t();v.add(o);o = null;循环申请Object 对象,并将所申请的对象放入一个Vector 中,如果仅仅释放引用本身,那么Vector 仍然引用该对象,所以这个对象对GC 来说是不可回收的。因此,如果对象加入到Vector 后,还必须从Vector 中删除,最简单的方法就是将Vector对象设置为null。Java引用类型:强引用:强引用指向的对象在任何时候都不会被系统回收。软引用:一个持有软引用的对象,不会被JVM很快地回收,jvm会根据当前堆的使用情况来判断何时回收。(可以用于实现对内存敏感的cache)弱引用:在系统GC时,只要发现弱引用,不管系统队空间是否足够,都会将

16、对象进行回收。虚引用:和没有引用几乎一样。weakHashMap:如果存放在weakHashMap中的key都存在强引用,那么weakHashMap就退化为HashMap。weakHashMap 会在内存紧张时,自动释放持有弱引用的数据。Egg:Map map = new WeakHashMap();List list = new ArrayList();for(int i=0;i<1000;i+)Integer ii = new Integer(i);list.add(ii);map.put(ii,new bytei); /当内存不时,他报OOM 因为存在强引用,而无法清理。Map m

17、ap = new WeakHashMap();List list = new ArrayList();for(int i=0;i<1000;i+)Integer ii = new Integer(i);map.put(ii,new bytei); /当内存不时,他会自动清理数据并发与线程安全:并发控制的方法:1.volatile本地内存:是java内存模型控制的一个抽象概念,并不是真实存在。 本地线程存储了该线程读写变量的副本。 每个线程都有一个私有的本地内存。 主内存:线程之间的共享的变量存储在主内存中。/线程1boolean stop = false;while(!stop)

18、0;   doSomething();/线程2stop = true;可能出现死循环:因为:每个线程在运行过程中都有自己的工作内存,那么线程1在运行的时候,会将stop变量的值拷贝一份放在自己的工作内存当中 那么当线程2更改了stop变量的值之后,但是还没来得及写入主存当中,线程2转去做其他事情了,那么线程1由于不知道线程2对stop变量的更改,因此还会一直循环下去。 /线程1boolean volatile stop = false;while(!stop)    doSomething(); /线程2stop

19、= true;但是用volatile修饰之后就变得不一样了:第一:使用volatile关键字会强制将修改的值立即写入主存;第二:使用volatile关键字的话,当线程2进行修改时,会导致线程1的工作内存中缓存变量stop的缓存行无效(反映到硬件层的话,就是CPU的L1或者L2缓存中对应的缓存行无效);第三:由于线程1的工作内存中缓存变量stop的缓存行无效,所以线程1再次读取变量stop的值时会去主存读取。那么在线程2修改stop值时(当然这里包括2个操作,修改线程2工作内存中的值,然后将修改后的值写入内存),会使得线程1的工作内存中缓存变量 stop的缓存行无效,然后线程1读取时,发现自己的

20、缓存行无效,它会等待缓存行对应的主存地址被更新之后,然后去对应的主存读取最新的值。 那么线程1读取到的就是最新的正确的值。Volatile 两层含义: 1)其他线程的修改能及时反映到当前线程上。2)对Volatile变量的修改,会及时写到主存。3)使用Volatile 将迫使所有线程均读写主存中的变量。但是不能保证原子性:public class Test     public volatile int inc = 0;         public void in

21、crease()         inc+;      public static void main(String args)         final Test test = new Test();        for(int i=0;i<10;i+)   

22、;         new Thread()                public void run()                     

23、;for(int j=0;j<1000;j+)                        test.increase();                   

24、         .start();            while(Thread.activeCount()>1)  /保证前面的线程都执行完            Thread.yield();    

25、60;   System.out.println(test.inc);    运行结果都不一致,自增操作不是原子性操作,而且volatile也无法保证对变量的任何操作都是原子性的2.同步关键字synchronized(内部锁) jvm虚拟机总会在最后自动释放synchronized锁。同步方法:public synchronized void method() 等价于:public void method()synchronized(this) 同步块:public void method()some code here;s

26、ynchronized(this) /仅同步必要的代码,有利于减小锁的竞争,提高吞吐量。other code here;Object的wait(),notify(),notifyAll() 函数wait():可以让线程等待当前线程上的通知,在wait()过程中,线程会释放对象锁。函数notify():将唤醒一个等待在当前对象上的线程,如果当前有多个线程等待,那么notify()方法将随机选择其中一个。notifyAll():唤醒所有等待在当前对象上的线程。实现一个阻塞队列:Public class BlockQueue()Private List list = new ArrayList();

27、Public snchronized bject pop()while(list.seze()=0) this.wait();/如果队列为空则等待。if(list.seze()>0) return list.remove(0);elsereturn null;Public snchronized void put(Object o)List.add(o);This.notify();/通知pop方法,可以取得数据3.ReentrantLock重入锁 -使用完后必须手动释放,一般放在finally里提供更多方法:Lock();/获得锁,如果已经占用,则等待。TryLock();/尝试获得锁

28、,如果成功则返回 true 否则 false 不等待。TryLock(带参数);/给定时间获得锁,如果成功则返回 true 否则 false 不等待。Unlock();/释放锁4.ReadWriteLock读写锁在读多写少的场合使用,读的绝对并行ReadWriteLock lock = new ReentrantReadWriteLock(); Lock read = lock.readLock(); Lock write = lock.writeLock(); 针对读写分别上锁。5.Semaphore信号量 -信号量是对锁的扩展ReentrantLock重入锁,synchronized(内部

29、锁)一次只允许一个线程访问一个资源,而信号量可以指定多个线程同时访问一个资源。通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。6.ThreadLocal线程局部变量ThreadLocal完全不提供锁,而是利用空间换时间的手段,为每个线程提供变量的独立副本。Get()Set()Remove()锁的优化:1.减小锁持有时间public void method()some code here;synchronized(this) /仅同步必要的代码,有利于减小锁的竞争,提高吞吐量。other code here;2.减小锁粒度ConcurrentHash

30、Map 将整个hashMap拆分成若干个段,每个段都是一个子hashMap.增加一个新的表项时,并不是将整个HashMap加锁,而是根据hashCode值,得到该表项应该放在那个段中,然后对该段加锁,并完成Put操作。在多线程下只要加入的数据不再同一段;就能做到真正的并行。缺点:访问全局时 性能差一些。3.锁粗化:减少锁的申请和释放的开销。synchronized(lock)For(int i=0;i<100;i+) For(int i=0;i<100;i+)synchronized(lock) 4.读写分离来代替独占锁在对多写少的情况下,用读写分离来代替独占锁。Java运行时数据区域:一、程序计数器(寄存器) -线程私有· 当前线程所执行的字节码行号指示器 · 线程执行Native方法时,计数器记录为空(Undefined)

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论