JVM模块理解

一、运行时数据区

1. 方法区

方法区:jdk1.8之前被称为永久代,1.8后被元空间代替
方法区主要用于存储类的结构信息如:字段、方法、常量池、静态变量等

1.1 永久代和元空间

二者都是Java中用于存储类信息的区域,区别:

存储空间:永久代可以理解为Java堆中的一块区域,元空间是直接在本地内存中分配的区域
GC方式:永久代回收基于标记-清除算法,元空间使用G1和CMS算法
内存大小:永久代大小是在JVM启动时确定的,元空间是根据本地内存动态的进行分配,但当本地内存被耗尽时仍会出现溢出,虚拟机抛出OutOfMemoryError:Metaspace异常
类型信息存储方式:永久代主要存储类型信息和方法信息等元数据,元空间则是将这些元素保存在本地内存中方便大量信息的处理

2. 堆

JVM管理最大的一块区域,用于存储对象的实例和数组
堆是线程共享的,分为新生代、老年代等,也称之为GC堆

3. 虚拟机栈

Java方法执行的内存模型,每个方法执行的时候都会创建栈帧,用于存储局部变量、操作数栈、动态链接、方法出入口信息等
栈帧随着方法的调用进行出栈操作
在这里插入图片描述

4. 本地方法栈

用于执行本地方法(非Java方法)
目的:完成java与外部程序的交互

5. 程序计数器

记录当前线程执行地址,线程私有的每一个线程都有一个程序计数器
(类似于闯关游戏的存档,保存当前程序执行的到的进度)

6. 运行时常量池

方法区的一部分,用于存放编译器生成的各种字面量和符号引用

7. 直接内存

通过native函数库直接分配堆外内存,直接内存不受java堆大小的限制,过于频繁地使用直接内存可能会导致本地内存耗尽,导致系统崩溃

二、类加载器

将java类加载都内存中,根据类的全限定名找到对应的字节码,并把其加载到jvm中进行解析、链接、初始化。
类加载器遵循委托机制(Delegation Model),即当一个类加载器接收到类加载请求时,会将这个请求让自己的父类加载器进行加载,如果父类加载器也存在父类加载器,则继续向上委托,直到达到最上层的类加载器停止,如果父类加载器可以完成类加载任务,则成功返回,如果
不能则让子类加载器尝试加载

优点:防止类被重复加载,在类加载时有明确的优先级关系,所以当父类加载成功后子类没必要重新加载
缺点:阻止了自定义类加载器的更新,会首先委派给父类;

1. 启动类加载器

它是JVM的一部分,负责加载Java核心类,如java.lang包中的类。它是由C++编写的,无法直接在Java代码中访问

2. 扩展类加载器

它负责加载JRE扩展目录(jre/lib/ext)下的JAR包中的类。可以通过Java代码获取到该类加载器的实例

3. 应用程序类加载器

也称为系统类加载器,它负责加载应用程序类路径(Classpath)上指定的类。通常情况下,Java开发者编写的代码都由该类加载器加载

4. 自定义类加载器

通过编写自定义的类加载器来实现特殊的加载需求,比如加载网络上的类、动态生成类等

三、垃圾回收

垃圾回收:GC;主要用于java的堆管理
堆:java管理的最大的一块内存空间,用于存放各种类的实例对象

1. 什么是垃圾回收

将java虚拟机中无用的对象清除,释放出占用的内存空间
GC是不定时的,是根据堆中对象占用空间大小、其他的特定条件触发。我们可以通过System.gc方法建议jvm执行垃圾回收

2. 新生代、老年代、永久代(方法区)区别

  • 老年代只有一个区域,新生代分为三个区域:Eden、From Survivor、To Survivor。新生代,老年代共同组成堆。堆是jvm管理的最大的内存空间,主要是用来存放各种类的实例对象。
  • 默认新生代、老年代比例为1:2,新生代占用1/3堆的大小,老年代占用2/3堆的大小
  • 新生代中 Eden、From Survivor、To Survivor的默认比例为 8:1:1可以通过参数 –XX:SurvivorRatio 来设定
  • jvm每一次都只会使用Eden和一块Survivor,总是有一块Survivor是空闲的
  • 永久代就是jvm的方法区,此区域中的数据比老年代和新生代中的数据更难回收

3. 分代的目的

根据不同的分代,选取最适合的垃圾回收算法,提高GC效率

  • 新生代:每一次垃圾回收都会有大量的对象被GC,所以采用复制算法,只需要少量的复制成本就可以完成
  • 老年代:老年代中对象存活率较高,采用复制算法话费的空间较多,采用标记整理、标记清楚算法

数据大部分情况都会被分配到老年代当中,除非当前数据再创建时需要分配的内存过大
当Eden区域没有空闲空间时jvm会触发MinorGc,当对象经过一次MinorGc还存活并且可以被Survivor接受那么会被移动到Survivor区域当中,此时当前对象年龄为1。每经过一次MinorGc,Survivor区域中的对象年龄都会加一,当年龄达到一定程度时(默认值为15),会被转移到老年代中

4. MinorGc、MajorGc、FullGc区别

  • MinorGc:新生代Gc,指发生在新生代的垃圾回收动作。由于新生代中java对象生命周期较短,所以MinorGc会频繁执行
  • MajorGc:老年代Gc,指发生在老年代的垃圾回收动作。通常MajorGc会连带着MinorGc一起执行,且比MinorGc慢得多
  • FullGc:清理整个对空间,包括新生代、老年代

5. MinorGc触发条件

  • 当Eden区域满了,或者新建对象时发现Eden区域不够用了,触发MinorGc

6. MajorGc、FullGc触发条件

  • 永久代空间不足
  • 有对象晋升老年代,但是老年代空间不足以存放
  • 堆内存分配对象过大
  • CMS GC异常
  • 执行了System.gc

7. 对象存活判断

引用计数法、可达性分析法

7.1 引用计数法

创建对象时,同时给对象创建一个计数器。当有引用指向对象时,计数器加一;当对象的引用被删除时,计数器减一

  • 优点:简单、特定情况下效率高
  • 缺点:很难解决对象之间的相互引用

7.2 可达性分析法

从GC Roots开始向下搜索,搜所走过的路径为引用链。当一个对象到GCRoots没有引用时,则表明这个对象可以回收,是不可用的
在这里插入图片描述
解决了对象之间循环引用问题

8. 垃圾回收算法

8.1 标记-清除算法

为每一个对象存储一个标记位,记录对象状态(存活/死亡)
第一阶段为标记阶段,检查对象是否死亡;第二个阶段为清理阶段,该阶段对死亡对象进行清理

  • 优点:可以解决循环引用问题;必要时才会进行回收
  • 缺点:回收时应用会挂起;标记清除的效率不高,尤其是当扫描对象比较多的时候;会造成内存碎片,虽然有空间但是连续性的空间不足以存储一个大的对象
  • 应用场景:一般应用于老年代中,因为老年代中的对象生命周期长、对象占用空间大,所以使用标记清除算法

8.2 标记-整理算法

为每一个对象存储一个标记位,记录对象状态(存活/死亡)
第一阶段为标记阶段,检查对象是否死亡;第二个阶段为整理阶段,将存活的数据整理移动到另一个存储空间中,之后将剩下的对象全部清除

  • 优点:解决了内存碎片问题
  • 缺点:因为更新了存活对象地址,所以需要更新引用
  • 应用场景:老年代中

8.3 复制算法

将内存划分为两块区域,对象只占用一块区域进行存储。当需要进行GC时,将存储对象的内存中存活的对象复制到另一块空闲的区域中,之后对原存储对象的内存清空。后续一直这么循环处理

  • 优点:解决了内存碎片、对象引用更新问题
  • 缺点:会造成一部分的内存空间浪费,可以根据实际情况对两个内存块比例进行调整;当存活对象数目较大时,复制性能算法性能会较差
  • 应用场景:一般应用于新生代中,因为新生代中的对象生命周期较短,复制时对象数据量不会太高;新生代中被分为三块区域->Eden、From Survivor、To Survivor,复制算法就在Eden->From 与 To之间进行;jvm在进行两块内存空间划分时不是按照1:1进行划分,一般是8:1:1,所以始终有90%的区域来对对象进行处理

9. 垃圾收集器

在这里插入图片描述

四、参数配置

JVM内存参数简述

#常用的设置
-Xms:初始堆大小,JVM 启动的时候,给定堆空间大小。 

-Xmx:最大堆大小,JVM 运行过程中,如果初始堆空间不足的时候,最大可以扩展到多少。 

-Xmn:设置堆中年轻代大小。整个堆大小=年轻代大小+年老代大小+持久代大小。 

-XX:NewSize=n 设置年轻代初始化大小大小 

-XX:MaxNewSize=n 设置年轻代最大值

-XX:NewRatio=n 设置年轻代和年老代的比值。如: -XX:NewRatio=3,表示年轻代与年老代比值为 13,年轻代占整个年轻代+年老代和的 1/4 

-XX:SurvivorRatio=n 年轻代中 Eden 区与两个 Survivor 区的比值。注意 Survivor 区有两个。8表示两个Survivor :eden=2:8 ,即一个Survivor占年轻代的1/10,默认就为8

-Xss:设置每个线程的堆栈大小。JDK5后每个线程 Java 栈大小为 1M,以前每个线程堆栈大小为 256K。

-XX:ThreadStackSize=n 线程堆栈大小

-XX:PermSize=n 设置持久代初始值	

-XX:MaxPermSize=n 设置持久代大小
 
-XX:MaxTenuringThreshold=n 设置年轻带垃圾对象最大年龄。如果设置为 0 的话,则年轻代对象不经过 Survivor 区,直接进入年老代。

#下面是一些不常用的

-XX:LargePageSizeInBytes=n 设置堆内存的内存页大小

-XX:+UseFastAccessorMethods 优化原始类型的getter方法性能

-XX:+DisableExplicitGC 禁止在运行期显式地调用System.gc(),默认启用	

-XX:+AggressiveOpts 是否启用JVM开发团队最新的调优成果。例如编译优化,偏向锁,并行年老代收集等,jdk6纸之后默认启动

-XX:+UseBiasedLocking 是否启用偏向锁,JDK6默认启用	

-Xnoclassgc 是否禁用垃圾回收

-XX:+UseThreadPriorities 使用本地线程的优先级,默认启用

JVM的GC收集器设置

-XX:+UseSerialGC:设置串行收集器,年轻带收集器 

-XX:+UseParNewGC:设置年轻代为并行收集。可与 CMS 收集同时使用。JDK5.0 以上,JVM 会根据系统配置自行设置,所以无需再设置此值。

-XX:+UseParallelGC:设置并行收集器,目标是目标是达到可控制的吞吐量

-XX:+UseParallelOldGC:设置并行年老代收集器,JDK6.0 支持对年老代并行收集。 

-XX:+UseConcMarkSweepGC:设置年老代并发收集器

-XX:+UseG1GC:设置 G1 收集器,JDK1.9默认垃圾收集器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值