【拒绝一问就懵】之你多少要懂点内存回收机制

背景介绍

  • Java优点之一就是其具备垃圾回收机制。在大部分状况下,JVM的GC(垃圾回收器)可以帮助咱们回那些不可到达的对象(就是未被引用的对象)。
  • 固然,在一些状况下,咱们仍然须要本身去释放内存(就是把对象引用置null,把容器、数组清空),不然就会引发内存泄漏,内存泄漏严重时将容易引起OutOfMemoryError,详情见 【拒绝一问就懵】之不可忽视的内存泄露
  • 此外,因为GC会中止全部的线程,包括UI线程,因此频繁的GC必然会致使画面卡顿(Android中每16ms为一帧),所以还应避免GC的频繁发生。一个致使GC频繁发生的缘由就 是【拒绝一问就懵】之没据说过内存抖动吧,点击连接看详情。
    GC时线程被中止示意图
  • 因此,理解Java的内存机制,有助于帮助咱们在写代码的过程当中避免内存泄漏。

走进内存模型

Java内存层级

JVM运行时内存划分

JVM运行时内存划分-详细
;

程序计数器

程序计数器是线程私有的内存区域,这个区域是Java虚拟机中惟一一个没有限制OutOfMemoryError的内存区域。之因此须要它是由于Java的多线程机制是经过轮流切换分配处理器执行时间来实现的,因此会涉及到线程的暂停和重启,而在一个线程中若是正在执行Java方法的话,这个计数器就回去记录当前正在执行的虚拟机字节码,一旦被暂停,恢复只须要从程序计数器记录的为止继续执行就能够。程序员

可是,若是线程中执行的是一个Native方法,那么程序计数器是不会去记录的,因此此时的程序计数器为空。算法

虚拟机栈Stack

Java虚拟机栈也是线程私有的。一条线程启动就会为它创建一个虚拟机栈。数组

在线程中每有一个Java方法被调用就会建立一个 “栈帧” 。每一个 “栈帧” 会保存执行该方法所需的局部变量表(通常Java程序员喜欢用这个部分来表明栈)、操做数栈、动态连接以及方法出口等信息。安全

若是一个线程中有过多的 “栈帧” 要入到虚拟机栈中,即短期内调用了过多的方法,就会形成 -- 栈益处 -- ,即 StackOverflowError 错误。bash

在这个内存区域中,若是虚拟机须要扩展内存,但没有申请到足够的内存,就会抛出 OutOfMemoryError 错误。多线程

本地方法栈

和虚拟机栈有些相似,但它是为Native方法提供服务的。并发

Java堆Heap

Java的堆内存是Java虚拟机所管理的内存中最大的一块。它是全部线程所共享的,用于存放对象实例和数组,Java虚拟机的GC主要就发生在这个地方。所以这块区域也叫作"GC堆"。工具

Java堆的内存能够按照垃圾回收算法【分代回收】分为【新生代区】和【老年区】,进一步的,【新生代区】能够分为【Eden区】、【From Survivor区】和【To Survivor区】。post

从内存角度来讲,Java堆内存又被划分为线程共享的内存区域和每一个线程私有的内存区域。spa

在Java堆区域中,若是没有内存分配给要建立的实例,而且堆也不可以再扩展,就会抛出OutOfMemoryError错误。

回收算法

Java8以后,Heap Segment真正意义上的是由Young GeneriationOld Generiation组成的。对象在其中是标记复制算法来断定一个对象是否应该被清理掉。
Heap Segment中发生的GC称为Major GC,只会影响Heap Segment区。

内存图

Young Generiation中的GC变化 — 复制算法

这个区域发生的GC称为Minor GC

  • 当对象被建立后,首先会被加入eden区。当eden区满了以后,就会触发一次GC,存活下来的对象会被复制到survivor区。
  • 当不为空的Survivor区满了,一样会触发一次GC。
  • 当短期内有大量对象建立和释放一样会形成内存抖动,会触发CG。
  • 如图所示,survivor有两个区域,其中一个老是保持为空。
  • 现假设两个Survivor区分别为S0,S1,而且首次GC时,eden区中存活的对象被复制到S0中。当再次发生GC时,S0和eden中仍然存活的对象就会被复制到空的S1中,此时S0为空;再次发生GC时,S1和eden中存活的对象将被复制到S0中,此时S1为空;再次发生GC...就是这样进行的。当一个对象被来回复制转移的次数达到阀值(默认为15次,能够经过使用-XX:MaxTenuringThreshold该命令来调整阀值)时,这个对象将被复制到Old Generiation区中,此时该对象将会变的相对安全,由于Old Segment区的GC频率相对较低。

Old Segment中的GC变化

这个区域发送的GC成为Full GC

  • 该区域满了以后会触发一次GC,在该次GC中,一些年龄较大的对象会被清理掉。
  • 若屡次触发GC后,该区域仍然处于满的状态,则会抛出OutOfMemoryError
  • 以两种状况下,新建对象会被直接复制到该区域中:
    • 当新建对象所须要的内存大于1/2的单个survivor区内存时。好比一些很长的对象;
    • 当新建对象被该区中的对象引用时,或者引用了该区域中的对象。

方法区

Java的方法区和Java的堆内存同样是被线程所共有的。它主要存放虚拟机加载的类信息、常量、静态变量、即时编译产生的代码等。

一些地方会将方法区合并到Java堆中一块儿去说。把它做为“永久代”。这在Hot-Spot虚拟机而言成立,可是通常来讲是不成立的。

Java的方法区若是内存不够分配的话,也是会抛出OutOfMemoryError错误的。也就是若是加载过多类到方法区的话,可能会形成方法区内存益处。

对象的可到达性

在GC检查对象的是否能够回收时,是根据对象是否可到达引用练顶端的GC Roots对象来判断的。GC Roots对象通常是虚拟机栈中变量表中引用的对象、类静态属性引用的对象、常量对象、JNI传到底层的对象。就是说,一个对象若是溯源不到这几种类型的对象的话,就认为它是没法到达的,那么它将会在GC时被回收。

新的引用类型

在JDK 1.2以后,Java扩充了4种引用类型定义:

强应用类型

即咱们平时经过new关键字建立出来的的对象的引用,只要强引用还存在,那么这些对象就必定不回被回收,即便时抛出OutOfMemoryError。何时强引用会不存在呢?当一个方法执行完,栈帧中的变量表将会被清理,在该方法中建立使用的临时强引用就会被清理掉,以后,本来它指向的对象就被变的不可到达。

软引用类型

用来描述一些有用但不是必须的对象,即经过SoftReference建立的对象,它们将会在本来肯定要发生内存溢出前的一次GC中被回收,若是回收完内存仍是不够,Java堆就会抛出OutOfMemoryError错误。就是说,在触发内存溢出发生前,这些对象是和强引用同样,只要引用还在,就不会被回收。

弱引用类型

用来描述一些没必要须的对象,即经过WeakReference建立的对象。弱引用对象的生命周期只有一次GC。

虚引用类型

一个对象的存在与否彻底不受虚引用的影响,它惟一的用处就是能够用来监测一个对象是否被回收。

方法区中的-运行时常量池

运行时常量池主要存放类中编译时期生成的常量,固然也能够动态的往里面添加。

好比:

"abc".intern();
复制代码

这个方法首先会检查运行时常量池中是否有这个字符串,有的话取出来用,没有的话生成一个并存到常量池中。

再好比,运行过程当中生成经过static修饰的String时,也会加入到常量池中。对于String而言,常量 + 常量 生成的也是常量,可是常量 + 变量 生成的就是变量了。

关于Dalvik虚拟机

Dalvik虚拟机是Google按照JVM虚拟机规范定制的虚拟机,它更符合移动设备的环境要求。与标准虚拟机不一样:

  • Dalvik编译生成的是.dex文件,这种格式的文件体积更小。而JVM规范的是.class文件。
  • Dalvik虚拟机是基于寄存器的,而JVM规范是基于栈的,因此速度方面会有优点。好比上面的说的标准Java虚拟机中,它的虚拟机栈就为线程的运行提供了服务。而Dalvik虚拟机中,使用寄存器去储存运行指令,同时寄存器也提供了程序计数器。寄存器是处理器的一部分哦!
  • Dalvik虚拟机容许在内存中建立都个实例,以隔离不一样的应用程序。这样,当一个应用程序在本身的进程中崩溃后,不会影响其它进程的运行。

关于ART虚拟机

ART虚拟机在Android 5.0之后是被默认开启的,此时Dalvik已经被Google放弃维护了。它与Dalvik虚拟机的不一样:

  • ART虚拟机在应用程序安装时就会把字节码经过dex2oat工具直接转成机器码储存,这个过程叫作AOT(Ahead-Of-Time)。而Dalvik是在每次启动应用程序时,经过传统的JIT(JUST IN TIME)模式将字节码转成机器码。显然,这样速度会慢很多。固然,ART虚拟机的占用内存也会更大些。
  • ART虚拟机在进行GC时采用了并法的模式。
    • 在传统的GC模式下,当虚拟机触发一次GC,会先暂停全部线程,而后检查全部对象,将符合回收条件的对象进行标记,而后进行回收,最后再恢复线程,这样的话gc速度会快些,可是遇到内存抖动,就会卡顿了。同时,传统的GC算法致使了【内存碎片化】严重,在一次回收后,不少内存块都会出现不连续的状况,这样会致使寻址变得困难,从而拖慢程序运行速度。
    • 而ART虚拟的垃圾回收算法容许GC时对对象的标记和一些对象的清理工做并发进行。同时,ART引入了【移动垃圾回收器】技术,使得碎片化内存可以被对齐,从而能稍微节约一些内存空间。

总结

  • Heap Segment被划分为两块:Young GeneriationOld Generiation
  • Young Genertiation中又被划分为Eden区和两个Survivor区,对象在其中采用标记复制算法来断定一个对象是应该清理仍是移到Old Generiation中。该内存区域发生GC的频率较高。
  • Old Generiation发生GC的频率相对较低。当有大对象被建立,或者和该区域有关的对象被建立时,它将会被直接移动到该区域中。

看到这里的童鞋快奖励本身一口辣条吧!

想要看CoorChice更多的文章,能够加个关注哦!

相关文章
相关标签/搜索