这种做的读书笔记,既不是我原创的,也不是我转载的,这种分类就很尴尬了。
内存溢出:
内存泄露:
3.1 概述
1. Lisp是第一门真正使用内存动态分配和垃圾收集技术的语言。
2. 程序计数器、本地方法栈、虚拟机栈这几个区域的内存分配和回收具备确定性
3. java堆和方法区则不一样,同一个接口的不同实现类所需要 内存可能不一样;一个方法中多个分支需要的内存也可能不一样;可能只有在程序运行时才能知道某些对象的创建,这部分的内存分配和回收都是动态的。
3.2 对象已死吗
1. 堆中放着java世界*几乎所有的*对象实例。(提问:还有哪些没有放在里面?)
3.2.1 引用计数算法
1. 引用计数实现简单:给定一个引用计数器,当某对象被引用时,计数器+1,当引用失效时则计数器-1 。当计数器为0时则该对象则不可能被引用。
2. 应用案例:微软的COM技术、Python语言、游戏脚本Squirrel、FlashPlayer。
3. 但JAVA没有使用主要是因为它不能解决对象之间的相互循环引用问题。(提问:这是个什么问题?)
3.2.2 可达性分析算法
1. 主流的商用程序语言(java、c#等)都是称通过可达性算法来判定对象是否存活。
2. 可达性算法就是从“GC Roots”开始,向下搜索,走过的路径叫做引用链。若某对象没有从”GC Roots”可到达的引用链,则该对象是不可用的。
3. 可作为GC Roots的对象包括:
虚拟机栈中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI引用的对象(提问:JNI是啥?)
(提问:这些root方式选择的先后次序?以怎样的方式选择?选择后是否会再次改变?)
3.2.3 再谈引用
JDK1.2之后对引用的概念进行了扩充,又将引用的类型分为以下四种:
1.强引用 。只要强引用还在,对象就永远不会被回收,类似于“Object o= new Object();”
2. 软引用。并不属于非必须对象,当系统将要发生内存溢出异常之前,将该部分对象列入回收范围进行二次回收,若回收后还是内存不够,则出现内存溢出异常。
3. 弱引用。属于非必须对象,它所关联的对象只能生存到下一次回收发生之前。当垃圾收集器工作时,无论当时内存是否充足,都将回收该部分对象。
4. 虚引用。不会对关联对象的生存时间产生影响,唯一目的是在该对象被回收时会收到一个系统通知。
3.2.4 生存还是死亡
1. 当某个对象被分析可达性算法标记为不可达对象时也不是“非死不可”。真正宣告一个对象的死亡是有两次标记。
1)一个对象A标记为不可达。
2)若A有以下两种情况则没有“拯救机会”,否则进入F-Queue队列中等待执行finalize()方法。
(1)已经执行过finalize()方法。
(2)没有覆盖finalize()方法。
3)进入F-Queue的对象,稍后由一个虚拟机自动创建的、低优先级的Finalizer线程去执行finalize()方法。这里的“执行”指会触发但是不一定会等它结束。如果A能够在finalize方法中拯救自己,即重新与引用链上的任何对象建立关联,则A会被移除“即将回收”集合。
2. 不建议使用finalize()方法。
3.2.5 回收方法区
1. 永久代垃圾回收的两部分:废弃常量、无用的类
2. 认定“无用类”的三个条件:
1)该类所有实例都被回收
2)加载该类的ClassLoader已被回收
3)该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
3. “无用类”是否被回收HotSpot虚拟机提供了-Xnoclassgc参数进行控制。还可以使用-verbose:class 、 -XX:+TraceClassLoading 、 -XX:+TraceClassLoading查看类加载和卸载信息。
(提问:反射、动态代理、ClassLoader)