您的位置:首页 > 编程语言 > Java开发

【gc】java 垃圾回收基础

2017-02-21 22:21 405 查看
1. 认识                   

                                                详情 参看oracle 官网原文 :Java Garbage Collection Basics

GC : Garbage Collection ,垃圾回收;

JVM:Java Virtural Machine,Java 虚拟机;相当于一个抽象的计算机,每个 JVM 实现一个 特定的 操作系统,JVM 负责将 Java 程序指令 编译为 可以在本地操作系统上运行的 指令和命令,这样 Java 程序就实现了 平台的独立性;

JRE :Java Runtime Environment ,Java 运行环境; JRE 由 JVM ,Java 平台核心类,以及支持 Java 平台的 类库组成;

JDK:Java Development Kit,Java 开发工具包;开发 Java 应用的一系列 工具集合;

1.1. 探索 JVM 架构

Hotspot 架构

Hotspot 是 一种主流的 JVM,sun 公司 目前使用的 JVM ;

Hotspot JVM  中使用了 热点代码探索技术(Hotspot);

Hotspot JVM 架构图(摘自oracle 官网)



更详细的架构图 (摘自网友)



从图中可以看出,JVM 的主要组成包括:

① classloader:类加载器,java 提供 三种 classloader

        Bootstrap classloader :顶层类加载器 ,负责加载核心类库,如java 的 rt.jar

        Extension classloader:扩展类加载器,负责加载  jre/lib/ext 中的类库

        Application classloader:系统类加载器,负责加载应用程序classpath 目录下的 jar 和 class 文件

② 运行数据区域(runtime data area):包括 

     method area :方法区对所有线程共享,用于存储 每个类的 结构,例如 运行时常量池,字段 和 方法 数据,以及方法和构造函数的代码;

原文:

It stores per-class structures such as the run-time constant pool, field and method data,
and the code for methods and constructors, including the special methods (§2.9) used in class
and instance initialization and interface initialization.


                     常量池:一个运行时常量池 是   每个类或者每个接口 运行时 常量池表的 表现,其中包含了几种常量;

     Heap:堆,当 JVM 启动时,创建 堆,堆对 JVM 所有线程所共享,该内存用于分配 所有的 类实例 和  数组

     Java Thread :java 线程;每个 JVM 线程在创建时都会创建一个 私有的 JVM  stack(栈),一个 JVM stack 保存着 帧(frame);JVM 栈中持有本地变量和 部分结果,并且是在 方法调用和 返回中起作用;JVM 栈 不能够直接被 操作,除了 pop 和 push 帧(frame),帧可能是已分配的 堆(堆的地址);

     Program Counter Registers:JVM 支持 多线程同时执行,每个 JVM  线程都对应着一个 pc register,用于记录当前 JVM 指令 执行的地址,如果当前被线程执行的方法是 native ,则 其值为 undefined;

     Native Internal Threads:本地方法栈,传统的 栈,通俗的说就是  C 栈,用来支持 native 方法(非 java 程序语言写的方法)

③ 执行引擎 (execution engine):包括

    JIT compiler

    Garbage Collector

专注于性能优化的三个部分:

    Heap : 堆 ,对象数据保存的地方 ;启动后 被 选定的 Garbage Collector 管理;大多数的优化方案是 堆的大小 和  选择最合适的 垃圾收集器(Garbage Collector)

    JIT Compiler:

    Garbage Collector:

1.2 性能 基础

通常,当优化一个 java 应用时, 两个主要关注点是 :  响应 和 吞吐量;

响应:专注于 响应的应用,长时间的等待是不可接受的;

吞吐量:专注于 吞吐量的应用,长时间的等待是可接受的;

2. 垃圾回收

自动垃圾回收 是  查看 堆内存的过程,标识出 哪些对象正在使用中 ,哪些对象没有在使用,然后删除没有在使用的对象;在使用中的对象 或者被引用的对象,意味着程序的某个部分仍然包含着指向该对象的指针;未在使用中的对象或者没有被引用的对象,不会再被程序中的任何部分所引用。所以对一个未被引用的对象 占用的内存可以进行回收;

回收步骤:

  ①  : marking  :标记

垃圾收集器标识出 哪些内存块正在使用中,哪些没有使用;

② : normal deletion :正常删除

移除未被引用的对象,保留被引用的对象和指向自由空间的指针;

②a  : deletion with compacting:压缩性删除

在删除未被引用对象之后,将剩余的 被引用的对象进行压缩;这样使得新内存的分配更容易而且更快;

2.1 为什么要分代 进行垃圾回收

由于不得不 标记和压缩 JVM 中所有的对象,这使得效率并不高,当越来越多的对象被分配,导致垃圾回收时间越来越长;

经过对应用的分析显示, 大多数的 对象生命周期很短;因此 ,堆(heap)被分成更小的部分 或者 代;

堆的被分成的部分:

         ①  Yong Generation:                新生代 ,包括 1个 eden 区域 和 2个 survivor 区域

新生代是 所有新的对象被分配的 和 老化的地方;当新生代被填满之后,会触发
minor garbage collection
;minor 垃圾收集会根据 对象的消亡率来优化处理的速度;存活(仍然被引用)的对象老化,并最终移到 老年代; 所有的 minor gc 事件都属于 "stop the world "事件;即所有的应用线程都会停止,直到操作完成;

         ② Old or Tenured Generation :老年代

老年代 被用来保存 长时间存活的对象,通常,新生代对象都设置了一个临界值,如果达到了这个临界值,这个对象便会被移动到 老年代。最终老年代会被收集。这个事件被称为 一个major garbage collection。major gc 也属于  Stop the world events ;通常 major gc 更慢,因为涉及了所有存活的对象;所以对于 追求响应的应用,major gc
应该最小化;

         ③  Permanent Generation:     永久代

永久代 包含了 JVM 需要的元数据 ,这些元数据用于描述应用中使用到的类和 方法;永久代是被  基于应用中使用到的类的运行时 JVM 填充的;同时,Java SE 类库类和 方法也可能保存在这里;如果 JVM 发现 类不再需要 并且 其他类需要空间,那么这个类就会被 收集(卸载);

图示如下(Note: java 1.8 中已经不存在 永久代,这部分的数据被从堆内存中转移到了 本地内存中,这样就避免了永久代的内存溢出的错误,而且jvm设置参数也有变化):



3. 分代垃圾回收过程

3.1 首先,任何新的对象都会被分配到 Young Generation 的 eden 区,两个 survivor  区 刚开始都是清空的;



3.2 当 eden 区 被分配满之后,一个  minor gc 被触发;



3.3 被引用的对象 被移到第一个 survivor 区(S0)。当 eden 被清空时,未被引用的对象会 被删除



3.4 当触发下一个 minor gc时,重复上面的动作:被引用的对象移动到 survivor 区,未被引用的对象被删除;不同的是,被引用的对象此时被移动到第二个 survivor 区(S1),而且来自于 上一个 minor gc 存在于 第一个 survivor 区(S0)的对象 的年龄(age)会增长并且会被移动到 第二个 survivor 区(S1);一旦所有幸存对象都被移动到 S1,S0 和 eden 就会被清空;注意 此时 在 survivor 区 中的对象拥有不同的年龄段;



3.5 下一个 minor gc, 重复同样的过程。但是这一次 survivor 区 会进行交换:也就是此时,被引用的对象会从 eden 和 S1 移动到 S0,幸存的对象 老龄化, Eden 和 S1 会被清空;



3.6 就这样重复的动作,如果一个 minor GC 之后,当老龄化的对象 达到了一定的年龄临界值(这里演示的为8,默认的为15),这些对象会从 新生代 提升为 老年代(Tenured)中;



3.7  minor GC 不断的产生对象 会 不断 提升 老年代 的 区域空间;



3.8 最终,一个 Major GC 会 清理  并且压缩 老年代;



4. 实例演示:略,参看原文过程;

5. Java 垃圾收集器种类

5.1 常见堆的 参数设置(jvm 1.8 不适用)(更详细的参数设置,可以参看网友的整理,JVM7,8详解及优化

SwitchDescription
-Xms
当 JVM 启动时,设置 堆 的初始化大小
-Xmx
设置最大的堆大小
-Xmn
设置新生代的大小
-XX:PermSize设置永久代的开始大小
-XX:MaxPermSize设置永久代的最大尺寸
5.2  Serial GC (串行 垃圾收集)

在 Java SE 5 和 Java SE 6 中 serial 收集器 是默认的 收集机制;使用该 收集器,minor 垃圾收集 和 major 垃圾收集 会 串行执行(使用了单一的虚拟 CPU);而且,他使用了标记压缩的收集方法;

命令行开关设置:开启使用 串行垃圾收集器

-XX:+UseSerialGC


5.3 Parallel GC (并行 垃圾收集):也被称为 吞吐量收集器

parallel garbage collector 使用 多线程 执行 新生代的垃圾收集。在 拥有 N核 CPU的主机上,Parallel 垃圾收集器 默认 使用 N 个 垃圾收集器线程;垃圾收集器线程可以使用命令行进行设置:

-XX:ParallelGCThreads=<desired number>
命令行开关设置:开启 使用 并行垃圾收集器

-XX:+UseParallelOldGC

5.4 Concurrent Mark  Sweep(CMS)Collector :

也被叫做  concurrent low pause  collector,用于收集 老年代。该收集器 尝试在应用线程执行的 同时进行 垃圾收集工作 ,以此来  最小化 因为 垃圾收集造成的停顿。正常该收集器 不会 拷贝 或者 压缩 存活的对象

开启 CMS 收集器:

-XX:+UseConcMarkSweepGC


设置使用的线程数

-XX:ParallelCMSThreads=<n>


5.5  G1  垃圾收集器

Garbage first 或者  G1 垃圾收集器 在 Java 7 使用到, 被设计用来替代 CMS 收集器;G1 收集器 是一个  并行的(parallel),同步的(concurrent),增量压缩低等待的 垃圾收集器;

开启 G1 垃圾收集器:

-XX:UseG1GC
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: