强引用、软引用、弱引用、虚引用。java
new一个Object存放在堆内存,而后用一个引用指向它,这个就是强引用。面试
若是一个对象具备强引用,那垃圾回收器不会回收它。当内存空间不足,java虚拟机宁愿抛出outOfMemoryError错误,使程序异常终止,也不会随意回收具备强引用的对象来解决内存不足的问题。算法
若是一个对象只具备软引用,则内存空间足够时,垃圾回收器就不会回收它,若是内存空间不足了,就会回收这些对象的内存。数组
只要垃圾回收器没有回收它,该对象就能够被程序使用。弱引用可用来实现内存敏感的高速缓存。缓存
只具备弱引用的对象拥有更短暂的生命周期。jvm
每次执行GC的时候,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会回收它的内存。不过,因为垃圾回收器是一个优先级很低的线程,所以不必定会很快发现那些只具备弱引用的对象。函数
"虚引用",形同虚设,与其余几种引用不一样,虚引用并不会决定对象的生命周期,若是一个对象仅持有虚引用,那么它就和没有任何引用同样,在任什么时候候均可能被垃圾回收器回收。.net
虚引用主要用来跟踪对象被垃圾回收器的活动。线程
内存空间小,线程私有。字节码解释器工做是就是经过改变这个计数器的值来选取下一条须要执行指令的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都须要依赖计数器完成3d
若是线程正在执行一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;若是正在执行的是 Native 方法,这个计数器的值则为 (Undefined)。此内存区域是惟一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 状况的区域。
线程私有,生命周期和线程一致。描述的是 Java 方法执行的内存模型:每一个方法在执行时都会床建立一个栈帧(Stack Frame)用于存储局部变量表、操做数栈、动态连接、方法出口等信息。每个方法从调用直至执行结束,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。
局部变量表:存放了编译期可知的各类基本类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型)和 returnAddress 类型(指向了一条字节码指令的地址)
StackOverflowError:线程请求的栈深度大于虚拟机所容许的深度。 OutOfMemoryError:若是虚拟机栈能够动态扩展,而扩展时没法申请到足够的内存。
区别于 Java 虚拟机栈的是,Java 虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。也会有 StackOverflowError 和 OutOfMemoryError 异常。
对于绝大多数应用来讲,这块区域是 JVM 所管理的内存中最大的一块。线程共享,主要是存放对象实例和数组。内部会划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)。能够位于物理上不连续的空间,可是逻辑上要连续。
OutOfMemoryError:若是堆中没有内存完成实例分配,而且堆也没法再扩展时,抛出该异常。
属于共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
属于方法区的一部分。
属于方法区一部分,用于存放编译期生成的各类字面量和符号引用。编译器和运行期(String 的 intern() )均可以将常量放入池中。内存有限,没法申请时抛出 OutOfMemoryError。
非虚拟机运行时数据区的部分
在 JDK 1.4 中新加入 NIO (New Input/Output) 类,引入了一种基于通道(Channel)和缓存(Buffer)的 I/O 方式,它可使用 Native 函数库直接分配堆外内存,而后经过一个存储在 Java 堆中的 DirectByteBuffer 对象做为这块内存的引用进行操做。能够避免在 Java 堆和 Native 堆中来回的数据耗时操做。 OutOfMemoryError:会受到本机内存限制,若是内存区域总和大于物理内存限制从而致使动态扩展时出现该异常。
从根节点扫描,只要这个对象在引用链,就是可达的。
java对象再也不被任何变量引用就进入了可恢复状态。
在回收该对象以前,该对象的finalize()方法进行资源清理。若是在finalize()方法中从新让变量引用该对象,则对象再次变为可达状态,不然改状态进入不可达状态。
java对象不被任何变量引用,且系统在调用对象finalize()方法后,依然没有使该对象变成可达状态,则该对象变成不可达状态。
当java对象处于不可达状态时,系统才会真正回收该对象所占有的资源。
算法分为标记和清除两个阶段:首先标记出全部须要回收的对象,在标记完成后统一回收全部被标记的对象。
不足:一个是效率问题,标记和清除两个过程的效率都不高,另外一个是空间问题,标记清除以后会产生大量不连续的内存碎片,空间碎片太多可能会致使之后再程序运行过程当中须要分配较大的对象时,没法找到足够的连续内存而不得不提早触发另外一次垃圾收集动做。 清除不是擦除,内容不会被立刻清空,直到有新的的内容写入才会覆盖。
它将可用内存按照容量划分为大小相等的两块,每次只使用其中的一块。当这块的内存用完了,就将还活着的对象复制到另外一块上面,而后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂状况,只要移动堆顶指针,按顺序分配内存便可。
不足:内存浪费。
实际中咱们并不须要按照1:1比例来划份内存空间,如堆内存的新生代,将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor,当另外一个Survivor空间没有足够空间存放上一次新生代收集下来的存活对象时,这些对象将直接经过分配担保机制进入老年代。
让全部存活的对象都向一端移动,而后直接清理掉端边界之外的内存。 主要用在老年代中
存活率低:青年代采用复制算法。 存活率高:标记清理/标记整理算法