前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java内存管理(二、Java垃圾回收)

Java内存管理(二、Java垃圾回收)

作者头像
bear_fish
发布2018-09-20 11:12:50
4470
发布2018-09-20 11:12:50
举报

二、Java垃圾回收 1. JVM运行环境中垃圾对象的定义      一个对象创建后被放置在JVM的堆内存中,当永远不再引用这个对象时,它将被JVM在堆内存中回收。或  当对象在JVM运行空间中无法通过根集合(root set)到达时,这个对象就被称为垃圾对象。 2. 堆内存 * 在JVM启动时被创建;堆内存中所存储的对象可以被JVM自动回收,不能通过其他外部手段回收 * 堆内存可分为两个区域:新对象区和老对象区     -- 新对象区可分为三个小区:Eden区、From区、To区     Eden区用来保存新创建的对象,当Eden区中的对象满了之后,JVM将会做可达性测试,检测有哪些对象由根集合出发是不可达的,不可达的对象就会被 JVM回收,并将所有的活动对象从Eden区拷到To区,此时一些对象将发生状态交换,有的对象就从To区被转移到From区。 3. JVM中对象的生命周期 * 创建阶段(步骤)     -- 为对象分配存储空间     -- 开始构造对象     -- 递归调用其超类的构造方法     -- 进行对象实例初始化与变量初始化     -- 执行构造方法体 * 应用阶段     -- 特征:系统至少维护着对象的一个强引用;所有对该对象引用强引用(除非显示声明为其它引用)     -- 强引用       指JVM内存管理器从根引用集合出发,遍寻堆中所有到达对象的路径。当到达某对象的任意路径都不含有引用对象时,对这个对象的引用就被称为强引用。 当内存不足时,JVM宁愿抛出OutOfMemeryError错误使程序停止,也不会靠收回具有强引用的对象来释放内存空间     -- 软引用       它能实现cache功能,防止最大限度的使用内存时引起的OutOfMemory异常,在内存不够用的时候jvm会自动回收Soft Reference。 软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,java虚拟机就会把这个软引用加入到与之关联的引用队列中。       Java中提供软引用的包:java.lang.ref.SoftReference(后续详解)      软引用       实现cache功能,防止最大限度的使用内存时引起的OutOfMemory异常,在内存不够用的时候jvm会自动回收Soft Reference.软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

Java代码 

  1. import java.lang.ref.SoftReference  
  2. //实现cache功能,最大限度利用内存
  3. Test test = new Test();  
  4. SoftReference sr = new SoftRefence(test);  
  5. test = null;  
  6. if(sr.get() != null){  
  7.      test = sr.get();  
  8. }else{  
  9.      test = new Test();  
  10.      sr = new SoftReference(test);  
  11.      test = null;  
  12. }  

[java] view plaincopy

  1. <span style="font-size: small;">import java.lang.ref.SoftReference  
  2. //实现cache功能,最大限度利用内存
  3. Test test = new Test();  
  4. SoftReference sr = new SoftRefence(test);  
  5. test = null;  
  6. if(sr.get() != null){  
  7.      test = sr.get();  
  8. }else{  
  9.      test = new Test();  
  10.      sr = new SoftReference(test);  
  11.      test = null;  
  12. }</span>  

Java代码 

  1. //创建一个强引用
  2. String str = new String("hello");   
  3. //创建引用队列, <String>为范型标记,表明队列中存放String对象的引用
  4. ReferenceQueue<String> rq = new ReferenceQueue<String>();   
  5. //创建一个弱引用,它引用"hello"对象,并且与rq引用队列关联
  6. //<String>为范型标记,表明WeakReference会弱引用String对象
  7. SoftReference<String> wf = new SoftReference<String>(str, rq);  
  8. str=null; //取消"hello"对象的强引用
  9. String str1=wf.get(); //假如"hello"对象没有被回收,str1引用"hello"对象
  10. //假如"hello"对象没有被回收,rq.poll()返回null
  11. Reference<? extends String> ref=rq.poll();   

[java] view plaincopy

  1. <span style="font-size: small;">//创建一个强引用
  2. String str = new String("hello");   
  3. //创建引用队列, <String>为范型标记,表明队列中存放String对象的引用
  4. ReferenceQueue<String> rq = new ReferenceQueue<String>();   
  5. //创建一个弱引用,它引用"hello"对象,并且与rq引用队列关联
  6. //<String>为范型标记,表明WeakReference会弱引用String对象
  7. SoftReference<String> wf = new SoftReference<String>(str, rq);  
  8. str=null; //取消"hello"对象的强引用
  9. String str1=wf.get(); //假如"hello"对象没有被回收,str1引用"hello"对象
  10. //假如"hello"对象没有被回收,rq.poll()返回null
  11. Reference<? extends String> ref=rq.poll();   
  12. </span>  

    -- 弱引用       只具有弱引用的对象有更短的生命周期,无论内存是否紧张,被垃圾回收器发现立即回收。弱引用可以和一个引用队列(ReferenceQueue)联合使用。       可分为长弱引用和短弱引用,长弱引用在对象的Finalize方法被GC调用后依然追踪对象       Java中提供弱引用的包:java.lang.ref.WeakReference     -- 虚引用       虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃圾回收的活动。       Phantom对象指一些执行完了finalize函数,并且为不可达对象,但是还没被GC回收的对象。这种对象可以辅助finalize进行一些后期的回收工作。 * 不可视阶段     -- 如果一个对象已使用完,并且在其可视区域不再使用,应该主动将其设置为null,即obj=null;这样可以帮助JVM及时地发现这个垃圾对象,并且可以及时地挥手该对象所占用的系统资源。

Java代码 

  1. package reference;  
  2. /*  
  3. WeakHashMap, 在这种Map中存放了键对象的弱引用,当一个键对象被垃圾回收,那么相应的值对象的引用会从Map中删除。WeakHashMap能够节约存储空间,可用 来缓存那些非必须存在的数据。  
  4. */
  5. import java.util.*;  
  6. import java.lang.ref.*;  
  7. class Key {  
  8.  String id;  
  9. public Key(String id) {  
  10. this.id = id;  
  11.  }  
  12. public String toString() {  
  13. return id;  
  14.  }  
  15. public int hashCode() {  
  16. return id.hashCode();  
  17.  }  
  18. public boolean equals(Object r) {  
  19. return (r instanceof Key) && id.equals(((Key) r).id);  
  20.  }  
  21. public void finalize() {  
  22.   System.out.println("Finalizing Key " + id);  
  23.  }  
  24. }  
  25. class Value {  
  26.  String id;  
  27. public Value(String id) {  
  28. this.id = id;  
  29.  }  
  30. public String toString() {  
  31. return id;  
  32.  }  
  33. public void finalize() {  
  34.   System.out.println("Finalizing Value " + id);  
  35.  }  
  36. }  
  37. public class MapCache {  
  38. public static void main(String[] args) throws Exception {  
  39. int size = 1000;  
  40. // 或者从命令行获得size的大小
  41. if (args.length > 0)  
  42.    size = Integer.parseInt(args[0]);  
  43.   Key[] keys = new Key[size]; // 存放键对象的强引用
  44.   WeakHashMap<Key, Value> whm = new WeakHashMap<Key, Value>();  
  45. for (int i = 0; i < size; i++) {  
  46.    Key k = new Key(Integer.toString(i));  
  47.    Value v = new Value(Integer.toString(i));  
  48. if (i % 3 == 0)  
  49.     keys[i] = k; // 使Key对象持有强引用
  50.    whm.put(k, v); // 使Key对象持有弱引用
  51.   }  
  52. // 催促垃圾回收器工作
  53.   System.gc();  
  54. // 把CPU让给垃圾回收器线程
  55.   Thread.sleep(8000);  
  56.  }  
  57. }  

[java] view plaincopy

  1. <span style="font-size: small;">package reference;  
  2. /*  
  3. WeakHashMap, 在这种Map中存放了键对象的弱引用,当一个键对象被垃圾回收,那么相应的值对象的引用会从Map中删除。WeakHashMap能够节约存储空间,可用 来缓存那些非必须存在的数据。  
  4. */
  5. import java.util.*;  
  6. import java.lang.ref.*;  
  7. class Key {  
  8.  String id;  
  9. public Key(String id) {  
  10. this.id = id;  
  11.  }  
  12. public String toString() {  
  13. return id;  
  14.  }  
  15. public int hashCode() {  
  16. return id.hashCode();  
  17.  }  
  18. public boolean equals(Object r) {  
  19. return (r instanceof Key) && id.equals(((Key) r).id);  
  20.  }  
  21. public void finalize() {  
  22.   System.out.println("Finalizing Key " + id);  
  23.  }  
  24. }  
  25. class Value {  
  26.  String id;  
  27. public Value(String id) {  
  28. this.id = id;  
  29.  }  
  30. public String toString() {  
  31. return id;  
  32.  }  
  33. public void finalize() {  
  34.   System.out.println("Finalizing Value " + id);  
  35.  }  
  36. }  
  37. public class MapCache {  
  38. public static void main(String[] args) throws Exception {  
  39. int size = 1000;  
  40. // 或者从命令行获得size的大小
  41. if (args.length > 0)  
  42.    size = Integer.parseInt(args[0]);  
  43.   Key[] keys = new Key[size]; // 存放键对象的强引用
  44.   WeakHashMap<Key, Value> whm = new WeakHashMap<Key, Value>();  
  45. for (int i = 0; i < size; i++) {  
  46.    Key k = new Key(Integer.toString(i));  
  47.    Value v = new Value(Integer.toString(i));  
  48. if (i % 3 == 0)  
  49.     keys[i] = k; // 使Key对象持有强引用
  50.    whm.put(k, v); // 使Key对象持有弱引用
  51.   }  
  52. // 催促垃圾回收器工作
  53.   System.gc();  
  54. // 把CPU让给垃圾回收器线程
  55.   Thread.sleep(8000);  
  56.  }  
  57. }  
  58. </span>  

4. Java中的析构方法finalize     finalize()方法常称之为终止器           protected void finalize(){               // finalization code here          }     对象即将被销毁时,有时需要做一些善后工作。可以把这些操作写在finalize()方法里。     Java终止器却是在对象被销毁时调用。一旦垃圾收集器准备好释放无用对象占用的存储空间,它首先调用那些对象的finalize()方法,然后才真正回收对象的内存。而被丢弃的对象何时被销毁,应用是无法获知的。大多数场合,被丢弃对象在应用终止后仍未销毁。到程序结束的时候,并非所有收尾模块都会得到调用。

5. 应用能干预垃圾回收吗?     在应用代码里控制JVM的垃圾回收运作是不可能的事。     对垃圾回收有两个途径。第一个就是将指向某对象的所有引用变量全部移走。这就相当于向JVM发了一个消息:这个对象不要了。第二个是调用库方法 System.gc()。第一个是一个告知,而调用System.gc()也仅仅是一个请求。JVM接受这个消息后,并不是立即做垃圾回收,而只是对几个垃圾回收算法做了加权,使垃圾回收操作容易发生,或提早发生,或回收较多而已。     希望JVM及时回收垃圾,是一种需求。其实,还有相反的一种需要:在某段时间内最好不要回收垃圾。要求运行速度最快的实时系统,特别是嵌入式系统,往往希望如此。     Java的垃圾回收机制是为所有Java应用进程服务的,而不是为某个特定的进程服务的。因此,任何一个进程都不能命令垃圾回收机制做什么、怎么做或做多少。

6. 垃圾回收算法 * 引用计数     该算法在java虚拟机没被使用过,主要是循环引用问题,因为计数并不记录谁指向他,无法发现这些交互自引用对象。     -- 怎么计数?         当引用连接到对象时,对象计数加1         当引用离开作用域或被置为null时减1     -- 怎么回收?         遍历对象列表,计数为0就释放     -- 有什么问题?         循环引用问题。 * 标记算法     标记算法的思想是从堆栈和静态存储区的对象开始,遍历所有引用,标记活得对象。     对于标记后有两种处理方式: (1) 停止-复制     -- 所谓停止,就是停止在运行的程序,进行垃圾回收     -- 所谓复制,就是将活得对象复制到另外一个堆上,以使内存更紧凑     -- 优点在于,当大块内存释放时,有利于整个内存的重分配     -- 有什么问题?         一、停止,干扰程序的正常运行,二,复制,明显耗费大量时间,三,如果程序比较稳定,垃圾比较少,那么每次重新复制量是非常大的,非常不合算     -- 什么时候启动停止-复制?         内存数量较低时,具体多低我也不知道  (2) 清除 也称标记-清除算法     -- 也就是将标记为非活得对象释放,也必须暂停程序运行     -- 优点就是在程序比较稳定,垃圾比较少的时候,速度比较快     -- 有什么问题?        很显然停止程序运行是一个问题,只清除也会造成很对内存碎片。     -- 为什么这2个算法都要暂停程序运行?        这是因为,如果不暂停,刚才的标记会被运行的程序弄乱

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2015年11月03日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档