Java自动内存管理机制

原创 2018年04月17日 15:59:43

自动内存管理机制

1. Java内存区域与内存溢出异常

1.1. 运行时数据区域

(1) 程序计数器:程序计数器用来记录当前线程执行的字节码行号。程序计数器是线程私有的,因为CPU通过时间轮转来为线程服务,为了线程切换后能够恢复的正确的位置,在每一个线程都保存一个程序计数器。如果执行的是本地方法则,程序计数器值为空。

(2) Java虚拟机栈:Java虚拟机栈是Java方法的内存模型,每一个方法被执行的过程都会创建一个栈帧用来存储局部变量表、操作栈、动态链接、方法出口等信息。局部变量表所需的内存空间是在编译时期分配的。Java虚拟机栈是线程私有的。如果申请的栈深度超过了虚拟机允许的最大栈深度会抛出Stack OverflowError。如果允许扩展时,当扩展时无法申请足够的内存会抛出OutOfMemoryError。使用-Xss来设置栈大小。

(3) 本地方法栈:与Java虚拟机栈相似,只不过是为本地方法服务的。

(4) 堆:Java堆是内存中线程共享的一块区域,在Java虚拟机启动的时候创建的。Java中的所有对象和数组都要在堆上分配。堆内存是GC的主要区域。由于GC是按代回收,所以堆还可以被细分为新生代和老年代。新生代又可以被细分为EdenFromSurvivorToSurvivor区域。使用-Xms-Xmx来设置堆的下限和上限。如果堆内存中没有足够的空间完成实例分配,并且也没法扩展就会抛出OutOfMemoryError异常。

(5) 方法区:是线程共享的一块区域。主要用来存储已被加载的类信息,常量,静态变量,即时编译器编译的代码。方法区一般被成为永久代,在这个区域也会进行垃圾回收,主要回收的是常量池,和对类型的卸载。方法区会出现OutOfMemoryError异常。使用-XX:PermSize-XX:MaxPermSize设置方法区的大小。

(6) 运行时常量池:是方法区中的一部分,已被加载的类信息包括,类的版本,字段,方法,接口等描述信息还有常量池。常量池用于存储编译时期生成的字面量,符号引用,在类加载后存放到方法区的常量池中。运行时也可以将新的常量加入到常量池中,String类的intern()方法。

2. 垃圾收集器与内存分配策略

2.1. 如何判断对象已死?

2.1.1. 两种判断方法

(1) 引用计数算法:在对象中保存一个引用计数器,如果该对象在一个地方被引用,则引用计数器值加1,如果有一个地方的引用失效则计数器减1。在任意时刻计数器的值为0则表示对象已死。优点是简单,速度快。缺点是:循环引用问题。

(2) 根搜索算法:当一个对象到GC Roots没有任何引用链,则表示该对象已死。

2.1.2. 哪些对象可以当做GC Roots

(1) Java虚拟机栈中的引用的对象。

(2) 本地方法栈中的引用的对象。

(3) 方法区中常量引用的对象。

(4) 方法区中静态变量引用的对象。

2.1.3. Java中的四种引用

(1) 强引用:例如Object obj = new Object();只要强引用还在,则对象一定不会被回收

(2) 软引用:当将要发生内存溢出时,GC则将这些对象列入垃圾回收的范围。如果回收后仍然内存空间不足,则抛出OutOfMemoryError异常。

(3) 弱引用:弱引用关联的对象只能活到下一次垃圾回收之前。

(4) 虚引用:虚引用完全不影响对象的生存周期,只是在垃圾回收时收到一个系统通知。

2.1.4. 对象的二次标记

当对象到GC Roots不可达时,并不一定被回收。还回经历两次标记的过程。

当对象到GC Roots不可达时,它会被第一次标记,并被筛选。筛选的条件是是否有必要执行finalize(), 如果对象没有覆finalize()或者已经被JVM执行了finalize(),则认为没有必要执行(直接被回收)。如果认为有必要执行,则将对象存放到一个F-Queue队列中,JVM会自动创建一个低级线程Finalizer用来执行finalize()。这里执行只是触发该方法,并不会等待该方法执行完成。执行finalize()方法是对象逃脱别回收的最后一次机会。GC 会对F-Queue中的对象进行二次标记,如果在期间被GC Roots引用链上的对象重新连接,则不会被回收。

2.2. 方法区的回收

方法区主要回收常量池和无用的类。

2.2.1. 如何判断一个类是无用的类

要满足下面三个条件:

(1) 该类的所有对象都已经被回收,Java堆中没有该类的任何实例。

(2) 该类的类加载器已经被回收。

(3) 该类的java.lang.Class对象没有在任何地方被使用。没有在任何地方通过反射访问该类的方法。

2.3. 垃圾回收算法

(1) 标记清除算法:第一步将所有要回收的对象进行标记,第二步回收掉所有被标记的对象。优点:简单;缺点:标记和清除效率都较低,并且会使得内存中出现很对碎片。

(2) 复制算法:将内存区域分成一个较大的Eden区域,两个较小的Survivor区域。分配空间时,每次使用Eden区域和其中一块Survivor区域。在垃圾回收时,将Eden区域和Survivor区域存活的对象复制到另一块Survivor中。

(3) 标记-整理算法:第一步对所有要回收对对象进行标记,第二步将存活的对象移到一端,将边界以外的所有对象回收。

(4) 分代收集算法。按照对象的生存周期将内存分成几个区域。在每个区域使用不同的算法。一般把Java堆分成新生代和老年代,对新生代使用复制算法,对老年代使用标记清除或者标记整理算法。

2.4. 垃圾收集器

(1) Serial收集器:是单线程的,使用的是复制算法。使用一条线程去垃圾回收时,必须要停止其他工作线程。

(2) ParNew收集器:是Serial的多线程版本。

(3) Parallel Scavenge:目标主要是用来控制CPU的吞吐量。使用的是复制算法

(4) Serial Old收集器:Serial的老年版本。使用的是标记整理算法。

(5) Parallel Old收集器:Parallel Scavenge的老年版本。使用的是标记整理算法。

(6) CMS收集器:以获取最短停顿时间为目标的。使用的是标记清除算法。在标记和清除阶段使用的是并发操作。

(7) G1收集器:Java堆分成若干个大小固定的区域,使用的是标记清除算法。跟踪没一个区域的垃圾堆积程度,并维持一个优先级队列,根据允许的收集时间,选择垃圾堆积最多的区域进行回收。

2.5. 内存分配与回收策略

2.5.1. 对象优先在Eden区域分配

对象优先在Eden区域分配,如果Eden区域没有足够的空间分配,则虚拟机发起一次Minor GC-XX:SurvivorRatio用来设置Eden区域和Survivor区域的大小比值。

Eden区域空间不足,发起一次Minor GC,将Eden区域和Survivor中存活的对象复制到另一个Survivor中,如果Survivor无法容纳所有存活的对象,则根据分配担保机制,将其转移到老年代。

2.5.2. 大对象直接进入老年代

大对象指需要大量连续内存空间的对象,例如大数组。使用-XX:PretenureSizeThreshold来设置阈值,如果对象答案与这个阈值则直接进入老年代。这样做的目的避免对象在Eden区域以及两个Survivor区域发生大量拷贝。

2.5.3. 长期存活的对象进入老年代

Java虚拟机给每个对象定义一个Age计数器,如果对象在Eden区域出生,经过一次GC仍然存活,将其复制到Survivor区域,如果能被容纳则将Age1。当Age的值大于等于MaxTenuringThreshold时进入老年代。阈值设置使用-XX:MaxTenuringThreshold

2.5.4. 动态对象年龄判定

对应并不总是等到年龄大于maxTenuringThreshold才进入老年代。如果Survivor中相同年龄的对象的总大小大于等于Survivor空间的一半,则将所有大于等于该年龄的对象移入到老年代。

2.5.5. 空间分配担保

在发生minor GC之前,Java虚拟机会检测之前每次进入老年代的平均大小是否大于老年代的剩余大小。如果大于老年代的大小,则将进行一次Full GC。如果不大于,则查看HandlePromotionFailure是否设置为true, 若果是则进行一次minor GC. 如果为False则进行一次Full GC

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_31638095/article/details/79976152

Java自动内存管理机制(一) JVM、Class

关于JVM的内存回收机制,内容概念非常多,非一篇博文所能详尽。所以这里只截取重要部分。详细之处可参阅相关书籍或博客: 《深入理解Java虚拟机-JVM高级特性与最佳实践》 周志明 著1.JVM概念 ...
  • u012440687
  • u012440687
  • 2016-07-25 09:03:17
  • 839

JVM_自动内存管理机制

运行时数据区域 java虚拟机在执行java程序的过程中会把它所管理的内存划分为若干个不同点数据区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则...
  • suneqing
  • suneqing
  • 2016-05-14 17:35:52
  • 1039

java自动内存管理机制

java自动内存管理 1.运行时的数据区域         java程序在运行的过程中把它所管理的内存划分为若干个区域分别是:         1. 方法区      2. 虚拟机栈       3....
  • peterchan88
  • peterchan88
  • 2016-10-31 10:14:30
  • 628

深入理解java虚拟机(一)--自动内存管理机制

java虚拟机运行时数据区java虚拟机在执行java程序时会将其管理的内存划分为若干不同的数据区域: - 程序计数器(Program Counter Register) - java虚拟机栈...
  • lansedeyouling
  • lansedeyouling
  • 2016-04-19 17:30:07
  • 317

深入理解Java虚拟机-自动内存管理机制

深入理解Java虚拟机-自动内存管理机制首先看下面的Java虚拟机运行时的数据区图,运行时数据区包含方法区、堆、虚拟机栈,本地方法栈,程序计数器。灰色区域是所有线程共享的数据区,是线程不安全的。另外三...
  • ECHO_FOLLOW_HEART
  • ECHO_FOLLOW_HEART
  • 2016-07-07 20:48:54
  • 458

.NET Framework 自动内存管理机制深入剖析 (C#分析篇)

在.NET Framework中,内存中的资源(即所有二进制信息的集合)分为"托管资源"和"非托管资源".托管资源必须接受.NET Framework的CLR(通用语言运行时)的管理(诸如内存类型安全...
  • syinter
  • syinter
  • 2002-11-21 09:50:00
  • 1362

自动内存管理机制

本文是阅读深入理解java虚拟机做的记录 Java 内存区域与内存溢出异常 1.java虽然不用进行new 出对象后的delete/free的操作了,但是有些部分还是需要进行手动操作的, 如文件的...
  • myrainblues
  • myrainblues
  • 2014-11-27 18:24:54
  • 881

C++ 内存管理机制

zhuanz 内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,C++高手从中获得了更好的性能,更大的自由,C++菜鸟的收获则是一遍一遍的检查代码和对C++的痛恨,但内存管理在C+...
  • w_y_x_2001
  • w_y_x_2001
  • 2014-04-04 08:36:49
  • 1101

Java自动内存管理机制(三) 对象、引用

垃圾收集器与内存分配策略 哪些内存需要回收? 什么时候回收? 如何回收?...
  • u012440687
  • u012440687
  • 2016-07-25 20:24:04
  • 344

java虚拟机----自动内存管理机制

java内存区域与内存异常之深入理解Java虚拟机JVM高级特性与最佳实践(周志明)心得 运行时数据区域 java虚拟机在执行java程序的时候,会把它所管理的内存划分为若干个不同的数据区域。...
  • yangzhichao888
  • yangzhichao888
  • 2018-02-22 19:55:06
  • 23
收藏助手
不良信息举报
您举报文章:Java自动内存管理机制
举报原因:
原因补充:

(最多只允许输入30个字)