JVM|GC

  • JVM
  • 1.JVM的五大内存区域
  • 2.新生代、老年代
  • 3.加载类的过程
  • 4.OOM异常
  • 5.JVM调优
  • GC
  • 1.判断怎样回收对象,是否可以回收?/可达性分析
  • 2.Java中的引用
  • 3、GC回收算法
  • 4.GC回收器
  • 5.Full GC和Major GC
  • 6.如何防止Full GC
  • 7.GC调优 参数设置


JVM

1.JVM的五大内存区域

JVM的五大内存区域分为:程序计数器、虚拟机栈、本地方法栈、堆区、方法区、

java 写内存地址 java内存地址_老年代


1,程序计数器

一块很小的内存空间,作用是当前线程执行方法的JVM指令地址

2,java栈

与程序计数器一样,java栈(虚拟机栈)也是线程私有的,其生命周期与线程相同。通常存放基本数据类型,对象引用(一个指向对象起始地址的引用指针或一个代表对象的句柄),returnAddress类型(指向一条字节码指令的地址)

栈区域有两种异常类型:如果线程请求的栈深度大于虚拟机所允许的深度,将抛StrackOverflowError异常;如果虚拟机栈可以动态扩展(大部分虚拟机都可动态扩展),当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常。

3,本地方法栈

与虚拟机栈作用很相似,区别是虚拟机栈为虚拟机执行java方法服务,而本地方法栈则是为虚拟机用到的Native方法服务。和虚拟机栈一样可能抛出StackOverflowError和OutOfMemoryError异常。

4,java堆

java Heap是jvm所管理的内存中最大的区域。JavaHeap是被所有线程共享的一块内存区域,在虚拟机启动时创建。主要存放对象实例。JavaHeap是垃圾收集器管理的主要区域,其可细分为新生代和老年代。如果在堆中没有内存完成实例分配,并且也无法再扩展时,会抛出OutOfMemoryError异常。

5,方法区

与javaHeap一样是各个线程共享的内存区域,用于存放已被虚拟机加载的类信息、常量、静态变量、及编译器编译后的代码等数据。当方法区无法满足内存分配的需求时,将抛出OutOfMemoryError异常。方法同时包含常听说的运行时常量池,用于存放编译期生成的各种自变量和符号引用。

6,直接内存

直接内存并不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域,是jvm外部的内存区域,这部分区域也可能导致OutOfMemoryError异常。

java 写内存地址 java内存地址_老年代_02

2.新生代、老年代

1> 新生代

主要是用来存放新生的对象。一般占据堆的1/3空间。由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收。

新生代又分为 Eden区、ServivorFrom、ServivorTo三个区。

Eden区:Java新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当Eden区内存不够的时候就会触发MinorGC,对新生代区进行一次垃圾回收。
ServivorTo:保留了一次MinorGC过程中的幸存者。
ServivorFrom:上一次GC的幸存者,作为这一次GC的被扫描者。

当JVM无法为新建对象分配内存空间的时候(Eden满了),Minor GC被触发。因此新生代空间占用率越高,Minor GC越频繁。

MinorGC的过程:采用复制算法。

首先,把Eden和ServivorFrom区域中存活的对象复制到ServicorTo区域(如果有对象的年龄以及达到了老年的标准,一般是15,则赋值到老年代区)
同时把这些对象的年龄+1(如果ServicorTo不够位置了就放到老年区)
然后,清空Eden和ServicorFrom中的对象;最后,ServicorTo和ServicorFrom互换,原ServicorTo成为下一次GC时的ServicorFrom区。

2 >老年代

老年代的对象比较稳定,所以MajorGC不会频繁执行。

在进行MajorGC前一般都先进行了一次MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC进行垃圾回收腾出空间。

MajorGC采用标记—清除算法:

首先扫描一次所有老年代,标记出存活的对象
然后回收没有标记的对象。

MajorGC的耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。

当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常。

3> 永久代

指内存的永久保存区域,主要存放Class和Meta(元数据)的信息。

Class在被加载的时候被放入永久区域。它和和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。

在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入 native memory, 字符串池和类的静态变量放入java堆中. 这样可以加载多少类的元数据就不再由MaxPermSize控制, 而由系统的实际可用空间来控制。

3.加载类的过程

类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的
方法区内,然后在堆区创建一个java.lang.Class 对象,用来封装类在方法区内的数据结构。
类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,.
并月向Java程序员提供了访问方法区内的数据结构的接口。

4.OOM异常

除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(OOM)异常的可能,

1,Java Heap 溢出

一般的异常信息:java.lang.OutOfMemoryError:Java heap spacess

java堆用于存储对象实例,我们只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,就会在对象数量达到最大堆容量限制后产生内存溢出异常。

出现这种异常,一般手段是先通过内存映像分析工具(如Eclipse Memory Analyzer)对dump出来的堆转存快照进行分析,重点是确认内存中的对象是否是必要的,先分清是因为内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。

如果是内存泄漏,可进一步通过工具(如Jrockit等工具)查看泄漏对象到GC Roots的引用链。于是就能找到泄漏对象时通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收。

如果不存在泄漏,那就应该检查虚拟机的参数(-Xmx与-Xms)的设置是否适当。

2,虚拟机栈和本地方法栈溢出

如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。

如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常

这里需要注意当栈的大小越大可分配的线程数就越少。

3,运行时常量池溢出

异常信息:java.lang.OutOfMemoryError:PermGen space

如果要向运行时常量池中添加内容,最简单的做法就是使用String.intern()这个Native方法。该方法的作用是:如果池中已经包含一个等于此String的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。由于常量池分配在方法区内,我们可以通过-XX:PermSize和-XX:MaxPermSize限制方法区的大小,从而间接限制其中常量池的容量。

4,方法区溢出

方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。

异常信息:java.lang.OutOfMemoryError:PermGen space

方法区溢出也是一种常见的内存溢出异常,一个类如果要被垃圾收集器回收,判定条件是很苛刻的。在经常动态生成大量Class的应用中,要特别注意这点。

5.JVM调优


GC

1.判断怎样回收对象,是否可以回收?/可达性分析

1)在Java中采取了可达性分析法.
通过一- 系列的“GC Roots”对象作为起点进行搜索,如果在“GC Roots”和一个对象之间没
有可达路径,则称该对象是不可达的,不过要注意的是被判定为不可达的对象不一定就会成
为可回收对象。被判定为不可达的对象要成为可回收对象必须至少经历两次标记过程,如果
在这两次标记过程中仍然没有逃脱成为可回收对象的可能性,则基本上就真的成为可回收对
象了。
2)虚拟机栈中引用的对象、方法区类静态属性引用的对象、方法区常量池引用的对象、本地
方法栈JNI引用的对象

2.Java中的引用

1、强引用
如果-一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM
也不会回收它,而是抛出OutOfMemoryError 错误,使程序异常终止。如果想中断强引用和
某个对象之间的关联,可以显式地将引用赋值为null,这样一来的话,JVM在合适的时间就
会回收该对象。
2、软引用
在使用软引用时,如果内存的空间足够,软引用就能继续被使用,而不会被垃圾回收器
回收;只有在内存空间不足时,软引用才会被垃圾回收器回收
3、弱引用
具有弱引用的对象拥有的生命周期更短暂。因为当JVM进行垃圾回收,一旦发现弱引
用对象,无论当前内存空间是否充足,都会将弱引用回收。不过由于垃圾回收器是一个优先
级较低的线程,所以并不一-定能迅速发现弱引用对象
4、虚引用
顾名思义,就是形同虚设,如果-一个对象仅持有虚引用,那么它相当于没有引用,在任
何时候都可能被垃圾回收器回收。
虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有
虚引用,就会把这个虛引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是
否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用
已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动

3、GC回收算法

1)标记清除法

思想:标记所有的可达对象(存在引用的对象),则未被标记的对象就是不存在引用的垃圾对象,GC时清除所有未被标记的对象

GC过程 标记清除法的GC时经历标记 + 清除两个过程,先标记,后清除

优点:只存在循环引用不存在其他引用的对象不会被标记,解决了循环引用问题

缺点:可能会产生空间碎片(不连续的内存空间),不连续的内存空间在内存分配时的工作效率低于连续的内存空间,尤其是对大对象的的内存分配

java 写内存地址 java内存地址_方法区_03

2)复制算法

思想:将内存空间分为两块相同的存储空间,每次只使用一块,GC时,将正在使用的内存中的存活对象复制到另一块存储空间中,然后清除正在使用的空间的所有对象

GC过程:先复制,再清除

优点:存活对象相对少时,效率很高(因为需要复制的对象少),存活对象复制到另一空间时,解决了空间碎片问题

缺点:系统内存只能使用一半的内存空间,而且如果存活对象相对多的话,比较耗时

java 写内存地址 java内存地址_java 写内存地址_04

3)标记压缩算法

思想:标记压缩法是对标记清除法的优化,所以也叫标记清除压缩法。和标记清除法一样,先标记所有的可达对象(存在引用的对象),不同的是,标记完成后并不是直接清除未标记的垃圾对象,而是将所有的被标记的对象(即存活对象)压缩到内存空间的一端后在清理边界外所有的空间。

GC过程:分为标记+压缩 + 清除三个步骤

优点:解决了标记清除法带来的空间碎片问题,又不需要折损可使用空间(复制算法折损了可使用空间)

java 写内存地址 java内存地址_JVM_05

4.GC回收器


5.Full GC和Major GC

1.Minor GC

从年轻代空间(包括Eden和Survivor区域)回收内存被称为Minor GC :

当Eden区域满了,jvm无法为新对象分配内存,会触发Minor GC;
新生代好进行标记和复制操作,就不会存在内存碎片。
年轻代中指向永久代中的引用,在标记阶段就会忽略。
stop-the-world。原因是Eden区中对象认为是垃圾,不会复制到Survivor区或者老年代。如果相反,Eden区
大部分对象不符合GC 条件,那么 Minor GC指定的时间就比较长。每次Minor GC会清理年轻代的内存。

2 Major GC 和Full GC

Major GC: 清理老年代
Full GC: 清理整个堆内存,包括年轻代和老年代

但是更多情况下,许多Minor GC 会 触发Major GC ,所以实际情况两者分离是不可能的。这就使得我们关注重点变成,GC是否能并发处理这些GC.

6.如何防止Full GC

-----------------------导致Ful1l GC一般有哪些情况?---------------------

1).新生代设置过小
一是新生代GC次数非常频繁,增大系统消耗;二是导致大对象直接进入旧生代,占据了
旧生代剩余空间,诱发Full GC
2).新生代设置过大
一是新生代设置过大会导致旧生代过小(堆总量一定),从而诱发FullGC;二是新生代GC
耗时大幅度增加
3). Survivor设置过小
导致对象从eden直接到达旧生代
4). Survivor设置过大
导致eden过小,增加了GC频率般说来新生代占整个堆1/3比较合适
GC策略的设置方式.
1).吞吐量优先可由-XX:GCTimeRatio=n来设置
2).暂停时间优先可由-XX:MaxGCPauseRatio=n来设置

------------------------Minor GC,Fu11 GC触发条件?-----------------------------

Minor GC触发条件:当Eden区满时,触发Minor GC。
FullGC触发条件:
(1)调用System.gc时,系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
(3)方法区空间不足
(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存
(5)由Eden区、From Space区向To Sp3ace区复制时,对象大小大于To Space 可存,则把
该对象转存到老年代,且老年代的可用内存小于该对象大小

7.GC调优 参数设置

emmm…