深入理解JVM虚拟机

来源:互联网 发布:数据库中shema是什么 编辑:程序博客网 时间:2024/05/01 10:30
前言

Java技术体系包括:

1、Java程序设计语言

2、Java虚拟机(各种硬件平台)

3、class文件格式

4、Java API类库

5、商业机构或者开源社区的第三方Java类库


JDK(Java Development Kit):用于支持Java程序开发的最小环境,包括1,2,4

JRE(Java Runtinme Environment):包括Java API类库中的Java SE API 和Java虚拟机两个部分。


Java虚拟机运行时区域内存包括:程序计数器,虚拟机栈,本地方法栈,Java堆,方法区。。其中程序计数器,虚拟机栈,本地方法栈三个是随着线程而生,随着线程而灭。所以深入理解Java虚拟机所讨论的内存分配和回收主要讨论Java堆和方法区,因为这两个部分的内存分配是动态的。


serial收集器:单线程,只会用一个CPU或一个线程实现垃圾收集。进行垃圾回收时,会暂停用户进程。

parnew收集器:在serial基础上增加了多线程,其实就是serial的多线程版本。无太多创新,但是只有serial和parnew能够和CMS收集器配合工作。CMS作为老年代收集器,serial或parnew作为新生代收集器。假如是多CPU环境就是用parnew,单个CPU使用serial,因为单个CPU环境下,parnew要花费时间在线程交互。

parallel  scavenge收集器:主要目标是达到一个可控制的吞吐量。吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。

CMS收集器:获取最短回收停顿时间为目标的收集器。使用标记-清除算法。优点:并发收集,低停顿。缺点:1、对CPU资源敏感。2、无法处理浮动垃圾。3、会产生大量空间碎片。

G1收集器:最前沿的收集器。面向服务端的收集器,特点:1、并行和并发。2、分代收集。3、空间整合,用的是标记-整理算法。4、可预测的停顿。



--------------------------------------------------------

1、Java 虚拟机中的运行时数据区包括:
  1. 程序计数器
  2. Java虚拟机栈
  3. 本地方法栈
  4. Java堆
  5. 方法区

其中程序计数器、Java虚拟机栈和本地方法栈是随着线程而生,随着线程而灭。所以Java垃圾回收器主要针对的是Java堆和方法区的回收。其中Java堆存放着几乎所有的对象和数组,而方法区中存放着类和常量。




2、GC机制
  1. 对象存活判定算法
    1. 引用计数算法 :每被引用一次就+1,当引用失效时就-1.当引用计数器为0时就不能再被使用,则回收掉。
    2. 可达性分析算法:通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,能一个对象到GC Roots有引用链时则为活的,没有就回收掉。
  2. 垃圾收集算法
    1. 标记-清理算法:先将需要回收的对象进行标记,然后统一回收掉被标记的对象。
      • 优点:简单
      • 缺点:1、效率低    2、会产生大量碎片,以至于后续有大的对象时没有足够的内存空间。
    2. 复制算法:将内存分成大小相等的两块,每次只用其中一块,当一块的内存用完之后,就讲还存活的对象复制到另外一块上面,然后再把已使用过的内存空间一次性清理掉。注意:复制到另一块内存后,对象时按顺序摆放的。
      • 优点:不会产生大量小的碎片
      • 缺点:1、内存代价很高,相当于将内存缩小为一半了    2、若对象存活率较高时,要进行较多的复制操作。
      • 改进的复制算法:将内存分为Eden(80%)和两个survivor(10%  *2),每次只使用一个survivor和Eden,当Eden和survivor使用完了之后,将存活的对象复制到另一个survivor上面,然后一次性清理掉Eden和survivor。这个一般用于清理新生代,因为98%的新生代是朝生夕死的,所以可以极大的提高内存的使用率。
    3. 标记-整理算法:当内存空间不够时,对存活的对象进行标记,然后将所有存活的对象都向一端移动,然后直接清理掉尾端边界以外的内存。
      • 优点:内存的利用效率很高
      • 缺点:移动对象需要花费时间,但应该不高。
    4. 分代收集算法:当前商业虚拟机的垃圾收集器都采用“分代收集”算法。把Java堆分为新生代和老年代,新生代采用改进的复制算法,因为每次垃圾收集时都发现有大批对象死去,只有少量存活。老年代采用标记-整理算法或者标记-清理算法,因为对象存活率高、没有额外空间对它进行分配担保。


3、垃圾收集器
  1. serial收集器(单线程的):进行垃圾收集时必须stop the world,即必须暂停所有的线程,知道它收集结束。新生代收集器!使用复制算法!
    • 优点:1、简单高效,对于限定在单个CPU环境来说,serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。      2、对于运行在client模式的虚拟机来说是一个很好的选择,因为停顿时间完全可以控制在几十毫秒最多一百多毫秒以内。
    • 缺点:进行垃圾收集时要暂停所有正在运行的线程。
  2. parnew收集器:相当于是serial的多线程版本。借鉴了很多serial的代码。是许多运行在server模式下的虚拟机的首选收集器。主要用于新生代收集。使用复制算法!
    1. 其中还有很大一个原因是因为除了serial之外只有parnew能跟CMS收集器配合使用。其中parnew收集新生代,CMS收集老年代。
    • 优点:能够进行多线程收集
    • 缺点:在进行收集的过程中还是会导致用户运行的线程暂停。
  3. parallel scavenge收集器:它关注的主要是达到一个可控制的吞吐量(吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)),停顿时间越短就越适合需要与用户交互的程序,而高的吞吐量可以高效的利用CPU时间,但是有可能存在停顿时间较长。所以parallel scavenge收集器主要用户后台运算而不需要太多交互的任务。新生代收集器!使用复制算法!
  4. serial old收集器:是serial收集器的老年代版本。主要进行老年代垃圾收集。使用标记整理算法。主要意思也是在于给client模式下的虚拟机使用。
    1. 两大用途:1、在JDK1.5以及之前的版本中与parallelscavenge收集器搭配使用     2、作为CMS收集器的后备预案,在并发收集发生concurrent mode failure时使用。
  5. parallel old收集器:是parallel scavenge收集器的老年代版本,主要进行老年代垃圾收集。使用多线程和标记-整理算法。
  6. CMS收集器是一种以获得最短回收停顿时间为目标的收集器,目前主要用子啊Java应用集中在互联网站或者B/S系统的服务端上,这些应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。老年代收集器!应用的是标记-清除算法!
    • 整个收集过程分为4步
      • 初始标记:stop the world,只是标记一下GC Roots能直接关联到的对象,速度很快。
      • 并发标记:进行GC Roots Tracing的过程,
      • 重新标记:stop the world, 为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录。停顿时间比初始标记阶段稍长,但远比并发标记的时间短。
      • 并发清除
    • 优点:由于消耗时间最长的并发标记和并发清除都能跟用户线程并发执行,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。     并发收集,低停顿。
    • 缺点:1、对CPU资源非常敏感。    2、无法处理浮动垃圾(指的是在并发标记过程中用户线程产生的垃圾,在这一次垃圾回收过程中没有被标记到,只能等到下一次垃圾回收时再处理。)        3、由于采用的是标记-清除算法,所以会产生大量的空间碎片。
  7. G1收集器是当今收集器技术发展的最前沿成果之一。是一款面向服务端应用的垃圾收集器。整体上看是基于标记-整理算法,从局部来看是基于复制算法实现的。
    1. 具备的特点:
      1. 并行与并发
      2. 分代收集:不需要和其他收集器配合就能独立管理整个GC堆。
      3. 空间整合
      4. 可预测的停顿


4、内存分配与回收策略
     Java技术体系中所提倡的自动内存管理可以归结为自动化的解决了两个问题:给对象分配内存以及回收分配给对象的内存。以上部分讲的都是回收内存,接下来讲分配内存。
     对象的内存分配,往大方向讲,就是在堆上分配,对象主要分配在新生代的Eden区上。分配的规则并不是百分之百固定的,其细节取决于当前使用的是哪一种垃圾收集器组合,还有虚拟机中与内存相关的参数的设置。

  1. 对象优先在Eden分配
  2. 大对象直接进入老年代:最典型的大对象指的是很长的字符串以及数组。这样做的目的是避免在Eden区以及两个survivor区之间发生大量的内存复制。
  3. 长期存活的对象将进入老年代:虚拟机给每个对象定义一个对象年龄计数器,如果对象在Eden出生并经过第一次minor GC后仍然存活,并且能被survivor容纳的话,就被移动到survivor空间中,对象年龄设置为1,在survivor区中每熬过一次minor GC,年龄就加1岁,当年龄增加到一定程度(默认为15岁),就会被移动到老年代中。
  4. 动态对象年龄判定:如果在survivor空间中相同年龄所有对象大小的总和大于survivor空间的一半,年龄大于等于该年龄的对象就可以直接进入老年代,无需等到要求的年龄。
  5. 空间分配担保:就是看老年代剩余连续空间是否比新生代所有对象总空间要大,是的话就进行minor GC。不是的话再检查handlepromotionfailure设置值是否允许担保失败,如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试进行minor GC,如果小于,则进行full GC。




















原创粉丝点击