最新要闻
- 【当前热闻】房东称闰二月要多交1个月房租 网友:这是住在月亮上吗?
- ChatGPT需要1万张NVIDIA A100显卡 国内仅有6家公司做到
- 全球快消息!杭州一男子坐地铁自带沙发 怎么过的安检?地铁回应
- 世界实时:20年来最优秀游戏处理器!AMD锐龙7 7800X3D首发评测:大幅超越i9-13900KS
- 周鸿祎离婚给前妻90亿 李国庆羡慕:没争夺控制权 他就乐吧
- RTX 3060登顶Steam神卡 AMD显卡被黑?竟是国内玩家的锅
- 全球快资讯丨“索要千万逼死老公案”五年后一审宣判:妻子翟欣欣退还男方上千万财产
- 天天精选!00后都开始立遗嘱了:微信号、QQ号、游戏账号成热门虚拟财产
- 《他是谁》烂尾 编剧疑似甩锅剧本总监:感谢你把剧本改成这
- 世界快讯:莱万:若留拜仁或会失去踢球的乐趣 在巴萨除了进球我有不同角色
- 【全球热闻】上映25周年纪念!《泰坦尼克号》4K重映版票房破2000万
- 每日热点:全球首例真人状告机器人!澳大利亚一市长准备告ChatGPT诽谤
- 胖东来创始人称加班不道德:不能只挣钱
- 世界热头条丨在Steam上买了个假冒黄游 结果居然给我玩爽了
- 焦点热文:女子称使用化妆品后流产 送检发现:汞含量超标30万倍!
- 每日消息!俄罗斯一男子19楼坠落后自行上救护车:还给医务人员唱了歌
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
【打怪升级】【jvm】关于jvm内存模型及GC调优
JVM调优,其实就是调整SWT和FGC的过程
JVM内存模型
通过一张基础的图了解最简单的JVM模型:
其实在jvm模型中,主要包含了我们常见的堆栈方法区等待--每个版本不同可能解释有所不同,这里默认以8版本为例:
首先给出官方文档的解释:
(资料图片仅供参考)
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.4
2.5.Run-Time Data Areas
The Java Virtual Machine defines various run-time data areas that are used during execution of a program. Some of these data areas are created on Java Virtual Machine start-up and are destroyed only when the Java Virtual Machine exits. Other data areas are per thread. Per-thread data areas are created when a thread is created and destroyed when the thread exits.
2.5.1.The
pc
RegisterThe Java Virtual Machine can support many threads of execution at once (JLS §17). Each Java Virtual Machine thread has its own
pc
(program counter) register. At any point, each Java Virtual Machine thread is executing the code of a single method, namely the current method (§2.6) for that thread. If that method is notnative
, thepc
register contains the address of the Java Virtual Machine instruction currently being executed. If the method currently being executed by the thread isnative
, the value of the Java Virtual Machine"spc
register is undefined. The Java Virtual Machine"spc
register is wide enough to hold areturnAddress
or a native pointer on the specific platform.2.5.2.Java Virtual Machine Stacks
Each Java Virtual Machine thread has a privateJava Virtual Machine stack, created at the same time as the thread. A Java Virtual Machine stack stores frames (§2.6). A Java Virtual Machine stack is analogous to the stack of a conventional language such as C: it holds local variables and partial results, and plays a part in method invocation and return. Because the Java Virtual Machine stack is never manipulated directly except to push and pop frames, frames may be heap allocated. The memory for a Java Virtual Machine stack does not need to be contiguous.
In the First Edition ofTheJava® Virtual Machine Specification, the Java Virtual Machine stack was known as theJava stack.
This specification permits Java Virtual Machine stacks either to be of a fixed size or to dynamically expand and contract as required by the computation. If the Java Virtual Machine stacks are of a fixed size, the size of each Java Virtual Machine stack may be chosen independently when that stack is created.
A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of Java Virtual Machine stacks, as well as, in the case of dynamically expanding or contracting Java Virtual Machine stacks, control over the maximum and minimum sizes.
The following exceptional conditions are associated with Java Virtual Machine stacks:
If the computation in a thread requires a larger Java Virtual Machine stack than is permitted, the Java Virtual Machine throws a
StackOverflowError
.If Java Virtual Machine stacks can be dynamically expanded, and expansion is attempted but insufficient memory can be made available to effect the expansion, or if insufficient memory can be made available to create the initial Java Virtual Machine stack for a new thread, the Java Virtual Machine throws an
OutOfMemoryError
.2.5.3.Heap
The Java Virtual Machine has aheapthat is shared among all Java Virtual Machine threads. The heap is the run-time data area from which memory for all class instances and arrays is allocated.
The heap is created on virtual machine start-up. Heap storage for objects is reclaimed by an automatic storage management system (known as agarbage collector); objects are never explicitly deallocated. The Java Virtual Machine assumes no particular type of automatic storage management system, and the storage management technique may be chosen according to the implementor"s system requirements. The heap may be of a fixed size or may be expanded as required by the computation and may be contracted if a larger heap becomes unnecessary. The memory for the heap does not need to be contiguous.
A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of the heap, as well as, if the heap can be dynamically expanded or contracted, control over the maximum and minimum heap size.
The following exceptional condition is associated with the heap:
If a computation requires more heap than can be made available by the automatic storage management system, the Java Virtual Machine throws an
OutOfMemoryError
.2.5.4.Method Area
The Java Virtual Machine has amethod areathat is shared among all Java Virtual Machine threads. The method area is analogous to the storage area for compiled code of a conventional language or analogous to the "text" segment in an operating system process. 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.
The method area is created on virtual machine start-up. Although the method area is logically part of the heap, simple implementations may choose not to either garbage collect or compact it. This specification does not mandate the location of the method area or the policies used to manage compiled code. The method area may be of a fixed size or may be expanded as required by the computation and may be contracted if a larger method area becomes unnecessary. The memory for the method area does not need to be contiguous.
A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of the method area, as well as, in the case of a varying-size method area, control over the maximum and minimum method area size.
The following exceptional condition is associated with the method area:
If memory in the method area cannot be made available to satisfy an allocation request, the Java Virtual Machine throws an
OutOfMemoryError
.2.5.5.Run-Time Constant Pool
Arun-time constant poolis a per-class or per-interface run-time representation of the
constant_pool
table in aclass
file (§4.4). It contains several kinds of constants, ranging from numeric literals known at compile-time to method and field references that must be resolved at run-time. The run-time constant pool serves a function similar to that of a symbol table for a conventional programming language, although it contains a wider range of data than a typical symbol table.Each run-time constant pool is allocated from the Java Virtual Machine"s method area (§2.5.4). The run-time constant pool for a class or interface is constructed when the class or interface is created (§5.3) by the Java Virtual Machine.
The following exceptional condition is associated with the construction of the run-time constant pool for a class or interface:
When creating a class or interface, if the construction of the run-time constant pool requires more memory than can be made available in the method area of the Java Virtual Machine, the Java Virtual Machine throws an
OutOfMemoryError
.See§5 (Loading, Linking, and Initializing)for information about the construction of the run-time constant pool.
2.5.6.Native Method Stacks
An implementation of the Java Virtual Machine may use conventional stacks, colloquially called "C stacks," to support
native
methods (methods written in a language other than the Java programming language). Native method stacks may also be used by the implementation of an interpreter for the Java Virtual Machine"s instruction set in a language such as C. Java Virtual Machine implementations that cannot loadnative
methods and that do not themselves rely on conventional stacks need not supply native method stacks. If supplied, native method stacks are typically allocated per thread when each thread is created.This specification permits native method stacks either to be of a fixed size or to dynamically expand and contract as required by the computation. If the native method stacks are of a fixed size, the size of each native method stack may be chosen independently when that stack is created.
A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of the native method stacks, as well as, in the case of varying-size native method stacks, control over the maximum and minimum method stack sizes.
The following exceptional conditions are associated with native method stacks:
- If the computation in a thread requires a larger native method stack than is permitted, the Java Virtual Machine throws a
StackOverflowError
.
- If native method stacks can be dynamically expanded and native method stack expansion is attempted but insufficient memory can be made available, or if insufficient memory can be made available to create the initial native method stack for a new thread, the Java Virtual Machine throws an
OutOfMemoryError
.
针对oracle的官方文档,给我们描述了jvm虚拟机的几个主要模块。那么从普通开发者的角度,其实这几大块分别干了这些事:
- Heap堆:
Java虚拟机具有在所有Java虚拟机线程之间共享的堆。堆是运行时数据区,从中为所有类实例和数组分配内存。堆可以是固定大小的,或者可以根据计算的需要进行扩展,并且如果不需要更大的堆,则可以收缩。堆的内存不需要是连续的。Java虚拟机实现可以为程序员或用户提供对堆的初始大小的控制,以及如果堆可以动态扩展或收缩,则可以提供对最大和最小堆大小的控制。如果计算所需的堆数超过了自动存储管理系统所能提供的堆数,Java虚拟机将抛出OutOfMemoryError。
那么可以看出,堆的主要作用就是分配空间,属于运行时数据区,会将我们运行时的内存分配。
同时,堆内存又分为新生代和老年代,以Young和Old区分,其中新生代主要存放回收年龄较短或者一些新new的对象,而老年代则是存放一些无法被gc的对象(考虑一下哪些对象会被放入老年代?)
堆内存,其实又被分为了三个区域:
Young:新生代;Old:老年代;Mate:元空间(永久代)
其中,新生代又分为这几段:
Eden:伊甸区;
s0、s1:survivor,用于YGC中复制移动;
默认,Eden:s0:s1是8:1:1的关系,而老年代和年轻代的比例默认是2:1
那么,什么时候会放入新生代什么时候会放入老年代呢?
默认,新创建的对象第一次会存放在新生代Eden中,我们认为新生代的对象百分之80都可能被回收掉,那么第一次YGC就会把Eden的对象先复制到s0,这是内存的复制,速度很快;
下一次YGC,又会把Eden和不为空的s0做YGC,因为我们认为大多数对象都要被回收,再把Eden和s0全部清空,那么没有回收的对象就位于s1;
再下一次.... 就是s0和s1来回倒。
但是,如果对象很大无法被放入新生代,或者它已经超过动态survivo的大小50%以上,我们就认为它不适合在新生代了,就会直接放去老年代。
同时,jvm的Object有对象头,对象头包含了比如说对象线程,锁,和一些gc年龄的属性,那么我们认为默认它的年龄到达15,它就是一个不可被回收的对象,那么就放入老年代;大对象也会放入老年代;还有如果Eden满了出发YGC,那么存活对象大小s0没办法承受也会将部分多余的对象放入老年代,我们认为老年代的对象很难被回收,那么什么对象可以出现在老年代呢?例如:spring生命周期的bean,常量(这里string常量池其实有些许变化),定义线程池、连接池这些都应该属于不可被回收。
- Stock栈(也叫虚拟机栈,线程栈)
每个Java虚拟机线程都有一个与线程同时创建的专用Java虚拟机堆栈。该规范允许Java虚拟机堆栈具有固定的大小,或者根据计算的需要动态扩展和收缩。如果Java虚拟机堆栈具有固定大小,则在创建每个Java虚拟机栈时,可以独立地选择该栈的大小。如果线程中的计算需要比允许的更大的Java虚拟机堆栈,则Java虚拟机会抛出StackOverflowError。如果Java虚拟机堆栈可以动态扩展,并且尝试进行扩展,但没有足够的内存可用于实现扩展,或者如果没有足够的存储器可用于创建新线程的初始Java虚拟机栈,则Java虚拟机将抛出OutOfMemoryError。
栈,顾名思义,是一种FILO的结构,那么在我们方法调用时,就会在栈内存中存储,包括一些对象的引用,栈中又有一个概念叫栈桢,什么是栈帧呢?
简单举例子就是说,比如我们有一个A方法调用B方法,B再调用C方法,那么这个栈帧可以这么表示:
并且,栈存储会随着线程的创建而创建,会随着线程的销毁而释放,不存在gc。这里其实有一个概念,比如说栈内存的OOM,会是什么样子?
如果一个递归,无法跳出递归或者递归数量太大,栈内存设置太小,是可能会抛出OOM的。
局部变量表:对应的就是方法参数和一些局部变量,因为这些都是线程私有的,所以不需要额外gc,随着线程结束被释放。
操作数栈:栈内的一些计算操作。
动态链接:一些引用?
方法返回地址:调用当前方法寄存器的值。
- Native Method Stock本地方法栈
与stock类似,只不过这里存储的是基于一些native之类的本地方法。
- Method Area 方法区/元空间
首先,明确一个概念,方法区/元空间,又可以叫做no-heap,它是用于与堆内存进行分开的概念!这个概念在jdk7叫做方法区,在jdk8叫做元空间,而且元空间不需要指定默认的大小了,而是会根据物理内存进行计算,当然如果物理内存不够了也会抛出OOM。并且,元空间是代替了7的老年代,其本质也是属于堆的一部分。
那么,为什么从7->8会有这样的变化呢?
1.永久代是有固定空间的,如果永久代的空间太小,-XX:MaxPermSize太小就会导致永久代OOM。
2.元空间使用的情况根据物理存储,最大没有限制,当物理内存达不到要求后同样会出发OOM。windows下,-XX:MetaspaceSize是21M,-XX:MaxMetaspaceSize的值是-1。
3.MetaspaceSize是初始化指定的大小,当达到了这个大小后,会触发FGC,同时会根据FGC的回收情况适当调整。所以线上如果频繁FGC,可能跟这个值有关,可以适当增大。
- STW
STW全名叫stop the world,它代表的意思是说在jvm触发GC时,会停止当前所有的用户线程,然后在gc完成后释放,那么在gc的时间段内就会发生所有的动作暂停无响应的情况。当然后面会有针对G1,ZGC以及一些回收算法可以并行的模式。
其中,Class对象 -- 永久代/元数据;
字符串常量 -- 1.7永久代;1.8在堆heap里;
在1.8之后,元数据不存在于堆,而是根据操作系统的内存进行管理的;
什么是垃圾
垃圾,顾名思义就是要被回收的对象,或者说要被回收的一组对象。
首先我们需要有个概念,java本身针对内存指针甚至内存空间的方式,都是基于unsafe或者其他的方式,我们在写java代码中不需要手动释放内存,这与c有很大的不同。
那么为什么java帮助我们去这么做呢?无非这么几点
1.忘记回收内存,那么这一块内存空间就会被占用无法释放。
2.多次回收,那么会不会将新的可用的数据回收掉?
3.更加简化开发。
那既然GC就是回收不可用的垃圾,是有jvm帮我们去完成的,那么jvm到底是如何确定什么是垃圾的呢?
主要有两种办法:
1.计数器,jvm会在对象头记录计数器,计数器代表引用计数器,那么如果这个计数器为0了,它没有被任何对象引用那么它就是一个可以回收的垃圾了。
2.GCROOT,根可达算法:试想一下如果有三个对象ABC,A持有B,B持有C,C持有A,但是它们再没有其他的对象引用了,那么它们的引用计数器不为0,但是它们其实算一堆垃圾,这样的话我们的根可达就派上用场了。比如我们根据Object,一直向下去找,那么找得到的对象就是可用的对象,那么其他的对象都可以称为不可用对象。什么概念呢?比如我们的局部变量 和我们的连接池 线程池 包括常量池 JNI指针持有的对象那这些一定是可用变量甚至加载的Clazz,一定不会被回收。
什么是对象头?
object header | Mark word(64bit) | klass pointer(64bit) |
normal object 普通对象,无锁。 | unused:25|identity_hashcode:31|unused:1|age:4|biased_lock:1|lock:2 | oop to metadata object 指向对象的元定义,可能会被指针指针压缩 |
biased object 带有偏向锁的对象 | thread:54 |epoch:2 |unused:1|age:4|biased_lock:1|lock:2 | oop to metadata object 指向对象的元定义,可能会被指针指针压缩 |
带有轻量锁的对象 | prt_to_lock_record:62 |lock:2 | oop to metadata object 指向对象的元定义,可能会被指针指针压缩 |
带有重量锁的对象 | prt_to_heavyweigth_monitor:62 |lock:2 | oop to metadata object 指向对象的元定义,可能会被指针指针压缩 |
GC | |lock:2 | oop to metadata object 指向对象的元定义,可能会被指针指针压缩 |
常见的垃圾回收算法
- 标记清除
首先对垃圾进行标记,然后进行回收,但是这种方式会导致内存间断,产生大量的内存碎片;这时当我们要分配一个大的对象时,可能会经历频繁的GC(目前大对象也可以直接扔在老年代中)
- copy复制
复制算法,相当于把可用的对象复制在另一个内存块中,然后直接将当前的内存清除;这样不会有内存碎片,但是缺点是内存占用会很大,最少需要将内存划分成两块进行复制移动,而且存活对象过多会导致效率低下
- 标记压缩
这时复制的升级版,其实它会将不可用对象清除后将可用对象向一端移动,这样的好处是不会有内存碎片并且不需要划分内存空间、但是效率会比较低
- 分代收集
这是最常用的算法,会根据内存空间进行划分,并且针对不同的内存选择不同的算法:例如新生代可以选用复制算法,默认新生代的对象很多都需要被回收;而老年代采用标记压缩或复制,腾出大片内存。
常见的垃圾回收器
- Serial Young:串行回收,会触发STW。
- Paraller Scavenge: ps,并行回收。
- ParNew配合CMS: 并行回收年轻代。
- Serial Old:串行回收,老年代。
- ParOld:并行回收,老年代。
- CMS:ConcurrentMarkSweep:并发回收,老年代。GC和用户进程同时进行,降低STW。
- G1:10ms 不区分老年代新生代
- ZGC:1ms 不区分老年代新生代
如何判断当前参数
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
-XX:+PrintCommandLineFlags:当前jvm参数
-XX:+PrintFlgsFinal :最终参数值
-XX:+PrintFlagsInitial:默认参数值
1.8版本默认使用的应该是Paraller GC 并行回收 : 默认使用 Parallel Scavenge(新生代)+ Serial Old(老年代)。
这里整理一个简单的表格,对这几种GC做简单的对比:
GC | 特性 | 描述 |
Paraller | 吞吐量 | 多线程STW |
G1(Garbage first) | 均衡 | 多线程SWT 并发 分代回收 |
ZGC(Z Garbage) | 延迟 | 所有并发 |
Shenandoah(jdk 12后) | 延迟 | 所有并发 |
Serial | 内存大小 启动时间 | 单线程STW |
Parallel GC是JDK 8以及更早版本的默认回收期。它专注于吞吐量,尽快完成工作,而很少考虑延迟(暂停)。
Parallel GC会在STW(全局暂停)期间,以更紧凑的方式,将正在使用中的内存移动(复制)到堆中的其他位置,从而制造出大片的空闲内存区域。当内存分配请求无法满足时就会发生STW暂停,然后JVM完全停止应用程序运行,投入尽可能多的处理器线程,让垃圾回收算法执行内存压缩工作,然后分配请求的内存,最后恢复应用程序执行。
G1 GC是JDK 9以后的默认回收期。G1试图平衡吞吐量和延迟。一方面,在STW暂停期间,依然会利用分代继续执行内存回收工作,从而最大化效率,这一点和Parallel GC相同;但是,它还会尽可能避免在暂停期间执行需要较长时间的操作。G1的长时间操作会与应用程序并行进行,即通过多线程方式,在应用程序运行时执行。这样可以大幅度减少暂停,代价是整体的吞吐量会降低一点。
所以,GC在8-9版本其实是一个分水岭,从9版本后默认使用G1,并且优化了G1处理的时间,包括G1处理大对象及老年代的时间。
那么,哪些场景下适用哪些垃圾回收器呢?
Serial:适用于单线程场景,简单的client客户端,内存小,没有过多的对象,单线程回收不需要线程切换的开销。
ParNew/Paraller :多CPU的服务器,可以采用多线程的方式回收,但是Paraller 追求的是短时间内尽量完成任务,那么就会有SWT时间,不适合交互型场景;ParNew降低了SWT时间,更适合交互场景。
Parallel Scavenge提供的参数
-XX:GCTimeRadio 直接设置吞吐量大小,GC时间占总时间比率.相当于是吞吐量的倒数.
-XX:MaxGCPauseMillis 设置最大GC停顿时间. Parallel Scavenge会根据这个值的大小确定新生代的大小.如果这个值越小,新生代就会越小,从而收集器就能以较短的时间进行一次回收;但新生代变小后,回收的频率就会提高,吞吐量也降下来了,因此要合理控制这个值.
-XX:+UseAdaptiveSizePolicy 通过命令就能开启GC 自适应的调节策略(区别于ParNew).我们只要设置最大堆(-Xmx)和MaxGCPauseMillis或GCTimeRadio,收集器会自动调整新生代的大小、Eden和Survior的比例、对象进入老年代的年龄,以最大程度上接近我们设置的MaxGCPauseMillis 或GCTimeRadio.
聊聊G1
首先一点,不论是新生代还是老年代,G1 ZGC等垃圾回收器是不区分内存类型的。
通过-XX:+UseG1GC 可以指定使用G1垃圾回收器。
G1首先具备压缩功能、避免碎片化内存问题、而且G1的SWT暂停时间可控、多线程GC、面向服务端应用比较友好、而且可以预测停顿的时间。
首先,G1会将所有堆内存划分成很多块大小相等的Region。每次要触发GC时,首先估算每个Region中可回收垃圾的数量、每次先从可回收最大的量开始回收,因此它的效率性能是很高的。
这样,其实在G1里,不再区分老年代新生代了,整个堆内存都是Region。但是衍生出了一个Humongous,它是特殊的Old,专门存放大型的对象。
这样的划分方式意味着不需要一个连续的内存空间管理对象.G1将空间分为多个区域,优先回收垃圾最多的区域. G1采用的是Mark-Copy,有非常好的空间整合能力,不会产生大量的空间碎片 G1的一大优势在于可预测的停顿时间,能够尽可能快地在指定时间内完成垃圾回收任务,通过jstat命令可以查看垃圾回收情况,在YGC时S0/S1并不会交换.
那么,如果一个对象,它自身和它持有引用的对象没有分配在一个Region中,我们是否需要遍历所有的Region才能进行一次GCRoot?每个Region上都有一个RememberSet,用于记录当前区域引用对象所在的区域。
G1的GC模式
1.YoungGC年轻代收集 在分配一般对象(非巨型对象)时,当所有eden region使用达到最大阀值并且无法申请足够内存时,会触发一次YoungGC。每次younggc会回收所有Eden以及Survivor区,并且将存活对象复制到Old区以及另一部分的Survivor区。
YoungGC的回收过程如下:
根扫描,跟CMS类似,Stop the world,扫描GC Roots对象。 处理Dirty card,更新RSet. 扫描RSet,扫描RSet中所有old区对扫描到的young区或者survivor去的引用。 拷贝扫描出的存活的对象到survivor2/old区 处理引用队列,软引用,弱引用,虚引用 2. mixed gc
当越来越多的对象晋升到老年代old region时,为了避免堆内存被耗尽,虚拟机会触发一个混合的垃圾收集器,即mixed gc,该算法并不是一个old gc,除了回收整个young region,还会回收一部分的old region,这里需要注意:是一部分老年代,而不是全部老年代,可以选择哪些old region进行收集,从而可以对垃圾回收的耗时时间进行控制。
G1没有fullGC概念,需要fullGC时,调用serialOldGC进行全堆扫描(包括eden、survivor、o、perm)。
何时使用G1
G1的第一个重要特点是为用户的应用程序的提供一个低GC延时和大内存GC的解决方案。这意味着堆大小6GB或更大,稳定和可预测的暂停时间将低于0.5秒。
如果应用程序使用CMS或ParallelOld垃圾回收器具有一个或多个以下特征,将有利于切换到G1:
Full GC持续时间太长或太频繁 对象分配率或年轻代升级老年代很频繁 不期望的很长的垃圾收集时间或压缩暂停(超过0.5至1秒) 注意:如果你正在使用CMS或ParallelOld收集器,并且你的应用程序没有遇到长时间的垃圾收集暂停,则保持与您的当前收集器是很好的,升级JDK并不必要更新收集器为G1。
关于jvm调优
关于jvm调优,我相信很多人甚至不会接触,因为毕竟有多少开发能直接操作线上服务器环境呢?可能也就是公司大牛级别的人了。
jvm本身东西很多,但是更多的说到jvm调优,我们主要是针对full GC 就是FGC的优化,至于YGC 是正常的,但是我们希望在应用服务中,更多的对象应该在YGC被回收,而不是无法回收全部放入FGC,因为FGC里的对象都是长期存活的,对应的FGC的时间也会更长!!同时还有一些基于jvm的参数,例如新生代中eden、s0、s1的大小,这些都会直接影响到对象是否会被直接扔在老年代中。当然,如果线上程序很稳定,jvm监控FGC的频率 时间都很正常,不建议修改jvm的参数!而且升级jdk版本也无需修改GC回收器!!
首先,我们要知道哪些会导致FGC
1.System.gc()方法的调用此方法的调用是建议JVM进行Full GC,虽然只是建议而非一定,但很多情况下它会触发 Full GC,从而增加Full GC的频率,也即增加了间歇性停顿的次数。强烈影响系建议能不使用此方法就别使用,让虚拟机自己去管理它的内存,可通过通过-XX:+ DisableExplicitGC来禁止RMI(Java远程方法调用)调用System.gc。
2.老年代空间不足在Survivor区域的对象满足晋升到老年代的条件时,晋升进入老年代的对象大小大于老年代的可用内存,这个时候会触发Full GC。,当执行Full GC后空间仍然不足,则抛出错误:java.lang.OutOfMemoryError: Java heap space 。为避免以上两种状况引起的FullGC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。
3.Metaspace区内存达到阈值
4.统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间 Survivor区域对象晋升到老年代有两种情况: (1)一种是给每个对象定义一个对象计数器,如果对象在Eden区域出生,并且经过了第一次GC,那么就将他的年龄设置为1,在Survivor区域的对象每熬过一次GC,年龄计数器加一,等到到达默认值15时,就会被移动到老年代中,默认值可以通过-XX:MaxTenuringThreshold来设置。 (2)另外一种情况是如果JVM发现Survivor区域中的相同年龄的对象占到所有对象的一半以上时,就会将大于这个年龄的对象移动到老年代,在这批对象在统计后发现可以晋升到老年代,但是发现老年代没有足够的空间来放置这些对象,这就会引起Full GC。 5.堆中产生大对象超过阈值这个参数可以通过-XX:PretenureSizeThreshold进行设定,大对象或者长期存活的对象进入老年代,典型的大对象就是很长的字符串或者数组,它们在被创建后会直接进入老年代,虽然可能新生代中的Eden区域可以放置这个对象,在要放置的时候JVM如果发现老年代的空间不足时,会触发GC。
6.老年代连续空间不足JVM如果判断老年代没有做足够的连续空间来放置大对象,那么就会引起Full GC,例如老年代可用空间大小为200K,但不是连续的,连续内存只要100K,而晋升到老年代的对象大小为120K,由于120>100的连续空间,所以就会触发Full GC。
那如何排查服务gc频率呢?
直接上arthas:
- Github:https://github.com/alibaba/arthas
- 文档:https://arthas.aliyun.com/doc/
https://arthas.aliyun.com/doc/vmoption.html
通过dashboard 以及命令可以排查gc的问题:
- 使用vmoption命令动态打开GC日志
$ vmoption PrintGC true
$ vmoption PrintGC trueSuccessfully updated the vm option. NAME BEFORE-VALUE AFTER-VALUE------------------------------------ PrintGC false true
$ vmoption PrintGCDetails true
$ vmoption PrintGCDetails trueSuccessfully updated the vm option. NAME BEFORE-VALUE AFTER-VALUE------------------------------------------- PrintGCDetails false true
使用vmtool强制GC
$ vmtool --action forceGc
[GC (JvmtiEnv ForceGarbageCollection) [PSYoungGen: 2184K->352K(76288K)] 19298K->17474K(166912K), 0.0011562 secs] [Times: user=0.01 sys=0.00, real=0.00 secs][Full GC (JvmtiEnv ForceGarbageCollection) [PSYoungGen: 352K->0K(76288K)] [ParOldGen: 17122K->16100K(90112K)] 17474K->16100K(166400K), [Metaspace: 20688K->20688K(1069056K)], 0.0232947 secs] [Times: user=0.14 sys=0.01, real=0.03 secs]
- 其他gc参数
$ vmoption PrintGCID true 打印GC ID
$ vmoption PrintGCID trueSuccessfully updated the vm option. NAME BEFORE-VALUE AFTER-VALUE-------------------------------------- PrintGCID false true
$ vmoption PrintGCDateStamps true 打印GC时间戳
$ vmoption PrintGCDateStamps trueSuccessfully updated the vm option. NAME BEFORE-VALUE AFTER-VALUE---------------------------------------------- PrintGCDateStamps false true
$ vmoption PrintGCTimeStamps true 打印GC启动时间
$ vmoption PrintGCTimeStamps trueSuccessfully updated the vm option. NAME BEFORE-VALUE AFTER-VALUE---------------------------------------------- PrintGCTimeStamps false true
- heapdump
打开HeapDumpBeforeFullGC开关,可以在GC前生成heapdump文件;打开HeapDumpAfterFullGC开关,可以在GC结束后生成heapdump文件
$ vmoption HeapDumpBeforeFullGC trueSuccessfully updated the vm option. NAME BEFORE-VALUE AFTER-VALUE------------------------------------------------- HeapDumpBeforeFullGC false true$ vmtool --action forceGc
再使用vmtool --action forceGc强制GC,则可以在GC日志中发现heapdump信息,并且在应用目录下会生成heapdump hprof
打开PrintClassHistogramBeforeFullGC开关,可以在GC前打印类直方图;打开PrintClassHistogramAfterFullGC开关,可以在GC结束后打印类直方图
$ vmoption PrintClassHistogramBeforeFullGC trueSuccessfully updated the vm option. NAME BEFORE-VALUE AFTER-VALUE------------------------------------------------------------ PrintClassHistogramBeforeFullGC false true$ vmtool --action forceGc
再使用vmtool --action forceGc强制GC,在GC日志中会打印类直方图,可以直观知道每个类的instances数量,占用内存大小:
#13: [Class Histogram (before full gc): num #instances #bytes class name---------------------------------------------- 1: 24519 5783400 [C 2: 5648 5102712 [B 3: 3685 888128 [Ljava.lang.Object; 4: 3255 619560 [I 5: 24263 582312 java.lang.String 6: 4227 475320 java.lang.Class 7: 1288 402112 [Ljava.util.HashMap$Node; 8: 75 296160 [Ljava.nio.channels.SelectionKey; 9: 6759 216288 java.util.HashMap$Node 10: 2069 182072 java.lang.reflect.Method 11: 3326 133040 java.util.LinkedHashMap$Entry
具体使用,参考arthas的使用文档。
常用JVM参数
堆设置:-Xmx3500m 设置JVM最大可用内存为3550M-Xms3500m 设置JVM堆内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存-Xmn2g 设置年轻代大小为2G-Xss128k 设置每个线程的堆栈大小‐XX:MetaspaceSize=256M 设置元空间大小‐XX:MaxMetaspaceSize=256M 设置元空间最大值-XX:NewRatio=4 设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)-XX:SurvivorRatio=4 设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4-XX:MaxPermSize=16m 设置持久代大小为16m-XX:MaxTenuringThreshold=0 设置垃圾最大年龄,如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代垃圾收集器:-XX:+UseParallelGC 选择垃圾收集器为并行收集器-XX:ParallelGCThreads=20 配置并行收集器的线程数-XX:+UseParallelOldGC 配置年老代垃圾收集方式为并行收集-XX:MaxGCPauseMillis=100 设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值-XX:+UseConcMarkSweepGC设置年老代为CMS并发收集-XX:+UseParNewGC 设置年轻代为并行收集。可与CMS收集同时使用日志打印:-XX:+PrintGC-XX:+PrintGCDetails-XX:+PrintGCTimeStamps-XX:+PrintGCApplicationStoppedTime 打印垃圾回收期间程序暂停的时间-XX:PrintHeapAtGC 打印GC前后的详细堆栈信息
例如:
1.将堆的最大、最小设置为相同的值,目的是防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间。 -Xmx3550m: 最大堆大小为3550m。 -Xms3550m: 设置初始堆大小为3550m。
2.在配置较好的机器上(比如多核、大内存),可以为老年代选择并行收集算法: -XX:+UseParallelOldGC 。
3.年轻代和老年代将根据默认的比例(1:2)分配堆内存, 可以通过调整二者之间的比率来调整二者之间的大小,也可以针对回收代。
比如年轻代,通过 -XX:newSize -XX:MaxNewSize来设置其绝对大小。同样,为了防止年轻代的堆收缩,我们通常会把-XX:newSize -XX:MaxNewSize设置为同样大小。
4.年轻代和老年代设置多大才算合理
1)更大的年轻代必然导致更小的老年代,大的年轻代会延长普通GC的周期,但会增加每次GC的时间;小的老年代会导致更频繁的Full GC
2)更小的年轻代必然导致更大老年代,小的年轻代会导致普通GC很频繁,但每次的GC时间会更短;大的老年代会减少Full GC的频率
如何选择应该依赖应用程序对象生命周期的分布情况: 如果应用存在大量的临时对象,应该选择更大的年轻代;如果存在相对较多的持久对象,老年代应该适当增大。但很多应用都没有这样明显的特性。
在抉择时应该根 据以下两点: (1)本着Full GC尽量少的原则,让老年代尽量缓存常用对象,JVM的默认比例1:2也是这个道理 。 (2)通过观察应用一段时间,看其他在峰值时老年代会占多少内存,在不影响Full GC的前提下,根据实际情况加大年轻代,比如可以把比例控制在1:1。但应该给老年代至少预留1/3的增长空间。
在实际过程中,我们并不频繁调整JVM参数,保证能够使用就好,当然在日常的监控中我们可以观察一下jvm中gc的频率,FGC的大小,根据具体的场景进行选择!正常情况下非必要不要去尝试调整,否则线上问题会很头疼。
关键词:
【打怪升级】【jvm】关于jvm内存模型及GC调优
【当前热闻】房东称闰二月要多交1个月房租 网友:这是住在月亮上吗?
ChatGPT需要1万张NVIDIA A100显卡 国内仅有6家公司做到
全球快消息!杭州一男子坐地铁自带沙发 怎么过的安检?地铁回应
焦点热讯:Lambda
TVM Deploy Runtime[施工中]
环球精选!股债二八平衡策略
世界实时:20年来最优秀游戏处理器!AMD锐龙7 7800X3D首发评测:大幅超越i9-13900KS
周鸿祎离婚给前妻90亿 李国庆羡慕:没争夺控制权 他就乐吧
RTX 3060登顶Steam神卡 AMD显卡被黑?竟是国内玩家的锅
全球快资讯丨“索要千万逼死老公案”五年后一审宣判:妻子翟欣欣退还男方上千万财产
环球热头条丨我的第一个项目(九) :飞机大战Vue版本塞到主页
Unity开发Hololens2—环境配置
设计模式(三十二)----综合应用-自定义Spring框架-自定义Spring IOC-自定义Spring IOC总结
CS50-Python实验3,4
天天精选!00后都开始立遗嘱了:微信号、QQ号、游戏账号成热门虚拟财产
《他是谁》烂尾 编剧疑似甩锅剧本总监:感谢你把剧本改成这
世界快讯:莱万:若留拜仁或会失去踢球的乐趣 在巴萨除了进球我有不同角色
【全球热闻】上映25周年纪念!《泰坦尼克号》4K重映版票房破2000万
每日热点:全球首例真人状告机器人!澳大利亚一市长准备告ChatGPT诽谤
Cesium 案例(二)Web MapTile Service with Time
渗透测试——简单的流程化信息收集
胖东来创始人称加班不道德:不能只挣钱
世界热头条丨在Steam上买了个假冒黄游 结果居然给我玩爽了
焦点热文:女子称使用化妆品后流产 送检发现:汞含量超标30万倍!
每日消息!俄罗斯一男子19楼坠落后自行上救护车:还给医务人员唱了歌
全球热文:速速电影院-尽情泡约网电影院
索尼推出WF-C700N无线耳机:15小时长续航、支持空间音频
GT2大探成最后一款!真我手机将不再有“大师探索版”
福布斯发布2023亿万富豪榜:亚马逊贝索斯最惨 资产流失近4000亿
焦点播报:AOC公布小苔藓M6迷你主机:i5-13420H、配全功能USB-C
101岁杨振宁罕见露面 获香港大学名誉博士学位
天天热门:java字节流和字符流
如何设计一个 70w 在线人数的弹幕系统 ?
vivo X Fold2通过3C认证:4800mAh电池+120W快充
当前快播:比尔盖茨回应暂停训练AI:并不能解决问题 重点是最大程度利用
今日最新!告别Mini LED!MacBook Pro屏幕将升级为OLED:三星供货
回国在即!旅美熊猫丫丫上海检疫后将被北京动物园接走
每日热文:汇编第三章复习之七种寻址
请注意!武汉全民健身中心游泳馆营业时间调整
199元一人!金山办公推出WPS 365“全家桶”服务
最资讯丨飞利浦推出新款44.5寸带鱼屏:弹出式摄像头 售价近9000元
头条焦点:真人电影《芭比》新预告:小丑女尽显修长美腿
女子请假照顾病危丈夫被公司开除!法院判了:公司赔13万
世界热议:sms-activate操作简便易上手且好用的接码工具【保姆级教程】
环球新消息丨5岁娃上坟藏冥币带回家要送朋友:还有男孩在奶奶坟前跪求不写作业不挨打
新势力老大换了!理想汽车周交付超6000辆:超蔚来、小鹏总和
环球热点评!2023年清明档票房破亿!任天堂《超级马力欧兄弟大电影》暂列亚军
天天热消息:立省4万!商家上线理想L7、L8“激光雷达”改装件:自己都能装
全球观天下!李子柒油管广告收益登顶热搜 停更一年收入78万元
世界热讯:宿迁贷款利率2022最新利率表,宿迁贷款利息
沙特俄罗斯等减产拉高油价后 黄金大涨近2%逼近历史最高纪录:美国难受
焦点速读:豆瓣8.0分!电影《忠犬八公》票房破亿:冯小刚主演
自建堆排序:
焦点!JavaScript快速入门(二)
微头条丨你坐过吗?这款双层火车全国仅此一列
焦点快播:必看?迪士尼《小美人鱼》内地定档5月26日 最新剧照黑小美人鱼亮相
全球热讯:时隔6年 任天堂马里奥之父宫本茂暗示新作要来了:告别手游平台
特斯拉法务部要玩真的了 马斯克:我们找了许多有才的律师
环球视点!666元起!国内一公司推出太空葬 创始人:逝去亲人快到头顶会手机提醒
福布斯发布2023亿万富豪榜 全球新首富诞生:要把股份平均分给五个子女
法拉利F1车手莱克莱尔签名时200万名表被抢走 小偷今落网
民主失真、政治失能严重侵蚀美式人权根基(钟声)——美国已成为全球人权发展的搅局者和阻碍者④
【独家】虚拟机磁盘扩容(parted、lvm)
环球今头条!【环球财经】英镑兑美元汇率创10个月新高
涨见识!清明节南北习俗有何不同:北方注重修坟 南方烧纸钱等
环球微头条丨特斯拉不要的技术 蔚来当个宝?
媒体:第一批因AI失业的人已经出现!公司不会养闲人 这些职业最容易被AI取代
在四川买一个新的房子买房子需要哪些手续费
天天观察:今日清明节 专家科普都有哪些习俗:其实是三个节日融合而来
升级iOS 16.4的用户被苹果坑了!Wi-Fi/天气崩溃、电池续航大缩水
环球看热讯:95后寿衣模特回应被说阴气重 本人回应坚持做自己:任何职业都该被尊重
世界微资讯!读SQL进阶教程笔记08_处理数列
【播资讯】NVIDIA全景光追模式成硬件杀手!RTX 4090仅16帧:不开DLSS没法玩
车主集体投诉宝马id7系统虚假宣传 宝马中国回应
速看:JR:我打NBA只因为热爱篮球 从来都不是为了钱和名利
为啥天空是蓝色的?而不是彩虹一般的七彩颜色呢?
环球聚焦:一亩地几百个洞 青藏高原的鼠害泛滥成灾:但其实不怪它们
全球实时:阿里搞出脱口秀版GPT 把“鸟鸟”塞进去了?官方回应
百事通!全球河马数量锐减 哥伦比亚却泛滥成灾:花350万美元送走70头
全球微速讯:含赶尸、傀儡 中式恐怖游戏《过阴》公开Demo试玩
工作总结经验材料(实用13篇)
微信封号解封的6种方法
天天百事通!游戏主播世界前30记录被熊孩子清零引围观 本人回应:痛苦又幸运
快讯:中国智能电视盒子销量榜:小米第三 腾讯太猛了
第一皇妃(关于第一皇妃的介绍)
世界热文:数据库系统原理之数据库应用设计与开发实例
别再用这姿势睡觉了 冲上热搜的“还阳卧”小心睡出毛病
天天热消息:周鸿祎突然离婚!360官方回应:12个月内不会减持
速读:解决Abp设置DefaultLanguage默认语言不生效的问题
【全球播资讯】光云科技:预计未来几年公司SaaS业务收入占比将持续提高
位居国内日本动画票房榜首:《铃芽之旅》获IGN 9分好评
天天时讯:富豪也玩不起大火箭!又一火箭公司破产
福布斯2023全球亿万富豪榜发布:新世界首富出炉!钟睒睒连续三年蝉联中国首富
天天微动态丨创始人周鸿祎离婚 360:周鸿祎拟将6.25%公司股份分割至胡欢名下
超级小桀回应"存档被清"上热搜:谢谢大家帮我想办法
java -- Object类和String类
债市日报:4月4日
全球看热讯:4月江南等地存在洪涝灾害风险 南方部分河流可能发生超警洪水
每日速看!精度优于10米 我国海上垂直回收火箭试验圆满成功