JVM参数说明

 

 

序号

 

JVM参数

 

参数说明

 

A

 

 

B

 

 

C

-XX:CMSInitiatingOccupancyFraction=60

设置CMS收集器在老年代空间被使用多少后触发垃圾收集,默认值68%jdk1.6之后为92%,仅在使用CMS时生效

-XX:CMSFullGCsBeforeCompaction=0

设置CMS收集器在进行若干几次垃圾收集后再启动一次内存碎片整理,仅在使用CMS时生效,默认0,即每次都整理

-XX:-CMSParallelRemarkEnabled

CMS为了减少第二次重新标记的暂停时间,可开启并行remark: -XX:+CMSParallelRemarkEnabled

-XX:ConcGCThreads=1

设置并行标记的线程数

-XX:CICompilerCount=3

设置最大JIT并行编译数,一般设置为1即可

D

-XX:+DisableExplicitGC

是否禁用System.gc()

E

-ea

-ea[:<package name>"..." | :<class name> ]

上述参数就用来设置jvm是否启动断言机制(从JDK 1.4开始支持),缺省时jvm关闭断言机制。

-ea 可打开断言机制,不加<packagename>classname时运行所有包和类中的断言,如果希望只运行某些包或类中的断言,可将包名或类名加到-ea之后。例如要启动包com.wombat.fruitbat中的断言,可用命令java -ea:com.wombat.fruitbat...<Main Class>

 

-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses

 

F

 

 

G

-XX:GCTimeRatio=19

GC时间占总时间的比率,默认值99,即允许1%GC时间。仅在使用Parallel Scavenge收集器时生效

-XX:G1NewSizePercent=5

设置要用作年轻代大小最小值的堆百分比。默认值是 Java 堆的 5%

-XX:G1MaxNewSizePercent=65

设置要用作年轻代大小最大值的堆大小百分比。默认值是 Java 堆的 60%

-XX:G1HeapRegionSize=32m

设置的 G1 区域的大小。值是 2 的幂,范围是 1 MB 32 MB 之间。目标是根据最小的 Java 堆大小划分出约 2048 个区域。

-XX:G1HeapWastePercent=5

设置您愿意浪费的堆百分比。如果可回收百分比小于堆废物百分比,Java HotSpot VM 不会启动混合垃圾回收周期。默认值是 10%

-XX:G1MixedGCLiveThresholdPercent=60

为混合垃圾回收周期中要包括的旧区域设置占用率阈值。默认占用率为 65%

H

-XX:HandlePromotionFailure

是否允许分配担保失败,即老年代的剩余空间不足以应付新生代整个EdenSurvivor区的所有对象都存活的极端情况

-XX:+HeapDumpOnOutOfMemoryError

JVM发生OOM时,自动生成DUMP文件

-XX:HeapDumpPath=/export/dump.txt

表示生成DUMP文件的路径,也可以指定文件名称

I

-XX:InitiatingHeapOccupancyPercent=10

设置触发标记周期的 Java 堆占用率阈值。默认占用率是整个 Java 堆的 45%

-XX:InitialHeapSize=130023424

初始堆大小,可带单位,默认应该是字节

J

 

 

K

 

 

L

 

 

M

-XX:MaxDirectMemorySize=10m

本机直接内存容量,默认和-Xmx一样

-XX:MaxTenuringThreshold=5

晋升到老年代的对象年龄,每经过Minor GC一次,年龄加1,当超过这个参数就进入老年代

-XX:MaxPermSize=1024M

最大非堆内存,或者永久代或方法区

-XX:MetaspaceSize=512m

元空间大小

-XX:MaxMetaspaceSize=1024m

最大元空间

-XX:MaxGCPauseMillis=500

设置GC的最大停顿时间。仅在使用Parallel Scavenge收集器时生效

-XX:MaxHeapSize=2048917504

最大堆大小

-XX:MaxNewSize=682622976

最大新生代

-XX:MinHeapDeltaBytes=524288

每次扩展堆的时候最小增长

N

-XX:NewSize=42991616

新生代大小

-XX:NewRatio=2

老年代和新生代空间大小比值

O

-XX:-OmitStackTraceInFastThrow

这是HotSpot VM专门针对异常做的一个优化,称为fast throw,当一些异常在代码里某个特定位置被抛出很多次的话,HotSpot Server CompilerC2)会用fast throw来优化这个抛出异常的地方,直接抛出一个事先分配好的、类型匹配的对象,这个对象的messagestack trace都被清空。可以明确:抛出这个异常非常快,不用额外分配内存,也不用爬栈。默认开启,关闭用“-”关闭

-XX:OldSize=87031808

老年代大小

P

-XX:PretenureSizeThreshold

直接晋升到老年代的对象大小,大于这个参数的对象将直接在老年代分配

-XX:ParallelGCThreads=1

设置并行GC时进行内存回收的线程数

-XX:PermSize=512M

非堆内存,或者永久代或方法区

-XX:+PrintGCDetails

详细打印GC日志

-XX:+PrintGC

粗略打印GC日志;-verbose:gc是他的别名

-XX:PreBlockSpin

更改自旋锁的自旋次数,使用这个参数必须先开启自旋锁-XX:+UseSpining

-XX:+PrintTLAB

表示可以看到TLAB的使用情况

Q

 

 

R

-XX:ReservedCodeCacheSize=240m

存储已编译方法生成的本地代码。代码缓存确实很少引起性能问题,但是一旦发生其影响可能是毁灭性的。如果代码缓存被占满,JVM会打印出一条警告消息,并切换到interpreted-only 模式:JIT编译器被停用,字节码将不再会被编译成机器码。因此,应用程序将继续运行,但运行速度会降低一个数量级,直到有人注意到这个问题。就像其他内存区域一样,我们可以自定义代码缓存的大小。相关的参数是-XX:InitialCodeCacheSize -XX:ReservedCodeCacheSize,它们的参数和上面介绍的参数一样,都是字节值。

S

-XX:SurvivorRatio=6

新生代Eden:Survivor=6比值

-XX:SoftRefLRUPolicyMSPerMB=50

Soft reference在虚拟机中比在客户集中存活的更长一些。其清除频率可以用命令行参数 -XX:SoftRefLRUPolicyMSPerMB=<N>来控制,这可以指定每兆堆空闲空间的 soft reference 保持存活(一旦它不强可达了)的毫秒数,这意味着每兆堆中的空闲空间中的 soft reference 会(在最后一个强引用被回收之后)存活1秒钟。注意,这是一个近似的值,因为 soft reference 只会在垃圾回收时才会被清除,而垃圾回收并不总在发生。系统默认为一秒,我觉得没必要等1秒,客户集中不用就立刻清除,改为 -XX:SoftRefLRUPolicyMSPerMB=0

-server

工作于服务器模式

-client

工作于客户机模式

T

-XX:+TraceClassLoading

表示查看类的加载信息

-XX:+TraceClassUnLoading

表示查看类的卸载信息

U

-XX: +UseParNewGC

打开此开关,使用ParNew+Serial Old收集器组合进行内存回收

-XX:+UseSerialGC

虚拟机运行在Client模式下的默认值,打开此开关,使用Serial+Serial Old的收集器组合进行内存回收

-XX:+UseConcMarkSweepGC

使用ParNew+CMS+Serial Old组合进行内存回收,Serial Old作为CMS出现Concurrent Mode Failure的后备收集器

-XX:+UseParallelGC

虚拟机运行在Server模式下的默认值,使用Scavenge+Serial Old(PS MarkSweep)收集器组合进行内存回收

-XX:+UseParallelOldGC

使用Parallel Scavenge+Parallel Old收集器组合

-XX:+UseAdaptiveSizePolicy

动态调整Java堆中各个区域的大小以及进入老年代的年龄; 这是一个开关参数,当这个参数打开后就不需要指定新生代的大小、Eden区和Survivor区的比例和晋升老年代对象的大小等细节参数了。虚拟机会根据当前系统运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大吞吐量,这种调节方式称为自适应调节策略。通常用于Parallel Scavenge

-XX:+UseCMSCompactAtFullCollection

设置CMS收集器在完成垃圾收集后是否进行一次内存碎片整理,仅在使用CMS时生效

-XX:+UseG1GC

使用G1收集器

-XX:+UseCompressedClassPointers

是否开启压缩类指针

-XX:+UseCompressedOops

压缩对象指针,oops是普通对象指针,Java堆中对象的对象指针被压缩到32bit,使用堆基地址(如果在低于26G内存空间中,为0)即,指针的偏移量针对于堆的基地址。注意:64bit服务器上设置-Xmx32g时,-XX:+UseCompressedOops-XX:+UseCompressedClassPointers会失效,所以最大的堆设置为31g

-XX:+UseFastUnorderedTimeStamps

 

-XX:-UseLargePagesIndividualAllocation

 

-XX:+UseCMSInitiatingOccupancyOnly

只是用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction),如果不指定,JVM仅在第一次使用设定值,后续则自动调整.

-XX:+UseStringDeduplication

在使用G1垃圾回收器的时候,通过 JVM参数 -XX:+UseStringDeduplication 我们可以通过删除重复的字符串,只保留一个char[]来优化堆内存。这个选择在Java 8 u 20被引入

-XX:+UseSpining

开启自旋锁

-XX:+UseTLAB

是否使用本地线程分配缓冲

V

 

 

W

 

 

X

-Xmn5g

新生代

-Xms600M

-Xms6g

最小堆

-Xmx600M

-Xmx6g

最大堆

-Xss128k

虚拟机栈大小

-Xnoclassgc

表示关闭JVM对类的垃圾回收

Y

 

 

Z

 

 

 

 

 

 

-Djava.net.preferIPv4Stack=true

优先使用IPv4

 

-Dsun.io.useCanonCaches=false

使用标准IO缓存

 

-Dfile.encoding=UTF-8

Utf-8编码输出

 

 

JITjust in time):即时编译器

JNDIjava native directory interfacejava 本地目录接口

TLABThread Local Allocation Buffer)内存分配缓冲区

 

 

 

一、内存分配的方式主要有两种:

一般取决于垃圾收集器是否支持标记整理算法

  1. 指针碰撞

一块内存区域,空闲的放一边,用过的放一边,中间用一个指针作为分界点指示器,分配内存类似于将空闲指针左右滑动

 

  1. 空闲列表

如果内存空闲区域不是规整的,相互交错,那就只能维护一个空闲列表记录可用的内存块了,比如CMS

 

对象的存储布局:

对象在堆上的存储布局主要分为3个区域:对象头,实例数据,字节对其填充

对象头:主要分为两个部分

第一部分:对象自身运行时的一些数据,固定长度,比如在32位虚拟机占32位,在64位虚拟机占64位,这部分区域(官方称Mark Word)主要存储对象的哈希码、GC分代年龄(占4bit、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。

第二部分:类型指针,指向对象的类元信息,如果对象是数组,还要记录数组长度

实例数据:一般父类的字段会出现在子类字段的前面,相同长度的字段会放在一起,顺序按声明顺序。

对其填充数据:HotSpot要求对象的存储起始地址是8字节的倍数,因此整个对象长度需要为8字节的整数倍

 

二、对象的访问定位

目前主要有两种方式,使用句柄或者直接指针,取决于虚拟机的实现

  1. 句柄

如果使用句柄,堆区会有一部分区域作为句柄池,句柄数据分为两部分,一部分指向堆内的对象实例数据,一部分指向方法区的类元数据。栈reference指向句柄,存储得是句柄地址。

优势:垃圾收集移动对象实例,只需要改变句柄内的对象实例地址,不用改变栈reference

  1. 直接指针(HotSpot采用)

reference直接指向对象实例地址,对象实例头中含有指向方法区类元信息的指针。

优势:速度更快,节省了一次指针定位的时间开销,由于对象的访问很频繁,此类开销还是很可观的

 

三、可达性分析

1.引用计数

2.可达性分析算法

GC Roots

主要是4中:虚拟机栈中的引用的对象、本地方法栈中引用的对象、类静态变量引用的对象、方法区常量引用的对象

 

判断一个类可以被垃圾回收主要看是否满足以下三个条件:

  1. 该类的实例全部被回收
  2. 加载该类的ClassLoader已经被回收
  3. 该类对应的Class对象没有在任何地方被引用,无法通过反射调用其方法等

 

四、枚举GC Roots

1.为了更快枚举所有的GC Roots,减少Stop The World的时间,HotSpot会用一种OopMap的数据结构存储所有的GC Roots

2.由于OopMap内容很容易变化,GC的时机很难确定,HotSpot引入了安全点(Safepoint的概念,即只有这些安全点才能GC,安全点的选取一般在方法调用、循环跳转、异常跳转等这些地方

3.安全点的引入带来另一个问题,GC需要Stop The World,需要暂停所有工作线程,所有线程不一定同时暂停在安全点,这就需要一种机制让所有线程都运行到安全点了之后,再让他们全部停下来,这里主要有两种中断方案:

4.抢先式中断(很少采用)

GC发生时,暂停所有线程,如果发现不在安全点,恢复线程,让其运行到安全点再暂停

5.主动式中断

GC需要中断线程的时候,设置一个标志位,每个线程在安全点去轮询这个标志位,决定是否暂停;轮询的时机选在需要分配内存的地方加上安全点

6.有了安全点,解决了程序运行中GC的问题,但是没有解决程序暂停时也需要GC的问题,这样引入了“安全区域”的概念,安全区域内引用关系一般不会变化,GC Roots关系比较稳定

 

 

  1. class文件的结构

再探JVM虚拟机

Class文件主要由无符号数和“表”组成,比如u11个字节,“表”一般以_info结尾,他是一种复合结构,其实也是无符号数的组合。

Class文件比较紧凑,有严格的规则,字节序采用大端序(Big Endian

再探JVM虚拟机

 

四、垃圾收集器

(1)Serial收集器(新生代)

单线程、工作时会暂停工作线程、简单高效、Client模式的最佳选择

 

(2)ParNew收集器(新生代)

Serial收集器的多线程版本、Server模式的最佳选择

 

(3)Parallel Scavenge收集器(新生代)

采用了复制算法、多线程、吞吐量优先

 

(4)Serial Old收集器(老年代)(和PS MarkSweep很相似,可以等同)

单线程、使用标记整理算法、Client模式

 

(5)Parallel Old收集器(老年代)

多线程、使用标记整理算法

 

(6)CMS收集器(老年代)

基于标记清除算法

 

(7)G1收集器(最新)

基于标记整理算法

再探JVM虚拟机

适合cpu密集型或者高吞吐量型

 

 

CMS收集器,简称并发低停顿收集器,以获取低停顿为目标,适合B/s架构和网站的服务端,基于标记-清除算法,主要分为以下4个步骤:

  1. 初始标记
  2. 并发标记
  3. 重新标记
  4. 并发清理

其中初始标记和重新标记仍然需要”stop the world”

  1. 初始标记详解:

初始标记仅仅标记GC Roots能直接关联的对象,速度非常快

  1. 并发标记

进行GC Roots Tracing的过程,耗时较长

  1. 重新标记

主要是修正在并发标记期间用户线程继续运行导致变动的那一部分对象记录,速度较快

  1. 并发清理

和用户线程并发执行,耗时较长

CMS也有几个小缺点

  1. cms对cpu资源较为敏感,因为是多线程并发
  2. CMS不能像其他老年代收集器那样,等到老年代满了才开始收集,因为需要预留空间给并发收集时的程序用,如果此时垃圾超过阈值,那么临时采用serial Old备用收集器
  3. 基于标记清除,容易产生内存碎片,所以需要每次fullGC后进行整理压缩

 

 

G1也是一个追求低停顿的收集器,整体上采用标记整理,局部region采用复制算法

G1收集器的收集步骤和CMS很相似,主要由以下几个步骤:

  1. 初始标记(很快)
  2. 并发标记(可并发)
  3. 最终标记(可并发)
  4. 筛选回收(可并发)

 

-XX:MetaspaceSize 初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。

-XX:MaxMetaspaceSize最大空间,默认是没有限制的。

 

除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性:

 

-XX:MinMetaspaceFreeRatio在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集

-XX:MaxMetaspaceFreeRatio在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集

 

3.MinMetaspaceFreeRatio

当进行过Metaspace GC之后,会计算当前Metaspace的空闲空间比,如果空闲比小于这个参数(即实际非空闲占比过大,内存不够用),那么虚拟机将增长Metaspace的大小。默认值为40,也就是40%。设置该参数可以控制Metaspace的增长的速度,太小的值会导致Metaspace增长的缓慢,Metaspace的使用逐渐趋于饱和,可能会影响之后类的加载。而太大的值会导致Metaspace增长的过快,浪费内存。

4.MaxMetasaceFreeRatio

当进行过Metaspace GC之后, 会计算当前Metaspace的空闲空间比,如果空闲比大于这个参数,那么虚拟机会释放Metaspace的部分空间。默认值为70,也就是70%

5.MaxMetaspaceExpansion

Metaspace增长时的最大幅度。在本机上该参数的默认值为5452592B(大约为5MB)。

6.MinMetaspaceExpansion

Metaspace增长时的最小幅度。在本机上该参数的默认值为340784B(大约330KB为)。

 

 可见在jdk8中:

1.字符串常量由永久代转移到堆中。

再探JVM虚拟机

常量池随永久代的变化

几种常量池:

1)静态常量池:即*.class文件中的常量池,在Class文件结构中,最头的4个字节存储魔数,用于确定一个文件是否能被JVM接受,接着4个字节用于存储版本号,前2个为次版本号,后2个主版本号,再接着是用于存放常量的常量池,由于常量的数量是不固定的,所以常量池的入口放置一个U2类型的数据(constant_pool_count)存储常量池容量计数值。

这种常量池占用class文件绝大部分空间,主要用于存放两大类常量:字面量和符号引用量,字面量相当于Java语言层面常量的概念,如文本字符串、基础数据、声明为final的常值等;符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:类和接口的全限定名、字段名称描述符、方法名称描述符。类的加载过程中的链接部分的解析步骤就是把符号引用替换为直接引用,即把那些描述符(名字)替换为能直接定位到字段、方法的引用或句柄(地址)。

2)运行时常量池:虚拟机会将各个class文件中的常量池载入到运行时常量池中,即编译期间生成的字面量、符号引用,总之就是装载class文件。为什么它叫运行时     常量池呢?因为这个常量池在运行时,里面的常量是可以增加的。如:“+”连接字符生成新字符后调用 intern()方法、生成基础数据的包装类型等等。

3)字符串常量池 :字符串常量池可以理解为是分担了部分运行时常量池的工作。加载时,对于class文件的静态常量池,如果是字符串就会被装到字符串常量池中。

4)整型常量池:Integer,类似字符串常量池。管理-128--127的常量。类似的还有CharacterLong等常量池(基本数据类型没有哦,DoubleFloat也没有常量池)

总结就是:

    class文件有常量池存放这个类的信息,占用了大多数空间。但是运行时所有加载进来的class文件的常量池的东西都要放到运行时常量池,这个运行时常量池还可以在运行时添加常量。字符串常量池、Integer等常量池则是分担了运行时常量池的工作,

在永久代移除后,字符串常量池也不再放在永久代了,但是也没有放到新的方法区---元空间里,而是留在了堆里(为了方便回收?)。运行时常量池当然是随着搬家到了元空间里,毕竟它是装类的重要等信息的,有它的地方才称得上是方法区。

 

其实,移除永久代的工作从JDK1.7就开始了。JDK1.7中,存储在永久代的部分数据就已经转移到了Java Heap或者是 Native Heap。但永久代仍存在于JDK1.7中,并没完全移除,譬如符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap

 

 

 

JVM一般是这样使用锁和Mark Word的:

 

1,当没有被当成锁时,这就是一个普通的对象,Mark Word记录对象的HashCode,锁标志位是01,是否偏向锁那一位是0。

 

2,当对象被当做同步锁并有一个线程A抢到了锁时,锁标志位还是01,但是否偏向锁那一位改成1,前23bit记录抢到锁的线程id,表示进入偏向锁状态。

 

3,当线程A再次试图来获得锁时,JVM发现同步锁对象的标志位是01,是否偏向锁是1,也就是偏向状态,Mark Word中记录的线程id就是线程A自己的id,表示线程A已经获得了这个偏向锁,可以执行同步锁的代码。

 

4,当线程B试图获得这个锁时,JVM发现同步锁处于偏向状态,但是Mark Word中的线程id记录的不是B,那么线程B会先用CAS操作试图获得锁,这里的获得锁操作是有可能成功的,因为线程A一般不会自动释放偏向锁。如果抢锁成功,就把Mark Word里的线程id改为线程B的id,代表线程B获得了这个偏向锁,可以执行同步锁代码。如果抢锁失败,则继续执行步骤5。

 

5,偏向锁状态抢锁失败,代表当前锁有一定的竞争,偏向锁将升级为轻量级锁。JVM会在当前线程的线程栈中开辟一块单独的空间,里面保存指向对象锁Mark Word的指针,同时在对象锁Mark Word中保存指向这片空间的指针。上述两个保存操作都是CAS操作,如果保存成功,代表线程抢到了同步锁,就把Mark Word中的锁标志位改成00,可以执行同步锁代码。如果保存失败,表示抢锁失败,竞争太激烈,继续执行步骤6。

 

6,轻量级锁抢锁失败,JVM会使用自旋锁,自旋锁不是一个锁状态,只是代表不断的重试,尝试抢锁。从JDK1.7开始,自旋锁默认启用,自旋次数由JVM决定。如果抢锁成功则执行同步锁代码,如果失败则继续执行步骤7。

 

7,自旋锁重试之后如果抢锁依然失败,同步锁会升级至重量级锁,锁标志位改为10。在这个状态下,未抢到锁的线程都会被阻塞。

 

 

相关文章:

猜你喜欢
  • 2021-05-27
  • 2021-09-01
  • 2022-02-24
  • 2021-06-14
  • 2021-05-29
  • 2021-09-10
相关资源
相似解决方案