• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

JVM 调优

JVM winrains 来源:周鑫磊 8个月前 (03-31) 119次浏览

1. 工欲善其事必先利其器

经过前面的各种分析学习,我们知道了关于JVM很多的知识,比如版本信息,类加载,堆,方法区,垃圾回收等,但是总觉得心里不踏实,原因是没看到实际的一些东西。
所以这一章节,咱们就好好来聊一聊关于怎么将这些内容进行直观地展示在我们面前,包括怎么进行相应的一些设置。OK,let’s go!

1.1 JVM参数

1.1.1 标准参数

-version
-help
-server
-cp

image.png

1.1.2 -X参数

非标准参数,也就是在JDK各个版本中可能会变动

-Xint 解释执行
-Xcomp 第一次使用就编译成本地代码
-Xmixed 混合模式,JVM自己来决定

image.png

1.1.3 -XX参数

使用得最多的参数类型
非标准化参数,相对不稳定,主要用于JVM调优和Debug

a.Boolean类型
格式:-XX:[+-]<name> +或-表示启用或者禁用name属性
比如:-XX:+UseConcMarkSweepGC 表示启用CMS类型的垃圾回收器
-XX:+UseG1GC 表示启用G1类型的垃圾回收器
b.非Boolean类型
格式:-XX<name>=<value>表示name属性的值是value
比如:-XX:MaxGCPauseMillis=500

1.1.4 其他参数

-Xms1000等价于-XX:InitialHeapSize=1000
-Xmx1000等价于-XX:MaxHeapSize=1000
-Xss100等价于-XX:ThreadStackSize=100

所以这块也相当于是-XX类型的参数

1.1.5 查看参数

java -XX:+PrintFlagsFinal -version > flags.txt

image.png

image.png

值得注意的是”=”表示默认值,”:=”表示被用户或JVM修改后的值
要想查看某个进程具体参数的值,可以使用jinfo,这块后面聊
一般要设置参数,可以先查看一下当前参数是什么,然后进行修改

1.1.6 设置参数的方式

  • 开发工具中设置比如IDEA,eclipse
  • 运行jar包的时候:java -XX:+UseG1GC xxx.jar
  • web容器比如tomcat,可以在脚本中的进行设置
  • 通过jinfo实时调整某个java进程的参数(参数只有被标记为manageable的flags可以被实时修改)

1.1.7 实践和单位换算

1Byte(字节)=8bit(位)
1KB=1024Byte(字节)
1MB=1024KB
1GB=1024MB
1TB=1024GB
(1)设置堆内存大小和参数打印
-Xmx100M -Xms100M -XX:+PrintFlagsFinal
(2)查询+PrintFlagsFinal的值
:=true
(3)查询堆内存大小MaxHeapSize
:= 104857600
(4)换算
104857600(Byte)/1024=102400(KB)
102400(KB)/1024=100(MB)
(5)结论
104857600是字节单位

image.png

1.2 常用命令

1.2.1 jps

查看java进程

The jps command lists the instrumented Java HotSpot VMs on the target system.
The command is limited to reporting information on JVMs for which it has the
access permissions.

image.png

1.2.2 jinfo

(1)实时查看和调整JVM配置参数

The jinfo command prints Java configuration information for a specified Java
process or core file or a remote debug server. The configuration information
includes Java system properties and Java Virtual Machine (JVM) command-line
flags.

(2)查看
jinfo -flag name PID 查看某个java进程的name属性的值

jinfo -flag MaxHeapSize PID
jinfo -flag UseG1GC PID

image.png

(3)修改
参数只有被标记为manageable的flags可以被实时修改
jinfo -flag [+|-] PID
jinfo -flag = PID
(4)查看曾经赋过值的一些参数

jinfo -flags PID

image.png

1.2.3 jstat

(1)查看虚拟机性能统计信息

The jstat command displays performance statistics for an instrumented Java
HotSpot VM. The target JVM is identified by its virtual machine identifier, or
vmid option.

(2)查看类装载信息

jstat -class PID 1000 10 查看某个java进程的类装载信息,每1000毫秒输出一次,共输出10次

image.png

(3)查看垃圾收集信息

jstat -gc PID 1000 10

image.png

1.2.4 jstack

(1)查看线程堆栈信息

The jstack command prints Java stack traces of Java threads for a specified Java
process, core file, or remote debug server.

(2)用法

jstack PID

image.png

  • DeadLockDemo
//运行主类
public class DeadLockDemo{
  public static void main(String[] args){
    DeadLock d1=new DeadLock(true);
    DeadLock d2=new DeadLock(false);
    Thread t1=new Thread(d1);
    Thread t2=new Thread(d2);
    t1.start();
    t2.start();
  }
}
//定义锁对象
class MyLock{
  public static Object obj1=new Object();
  public static Object obj2=new Object();
}
//死锁代码
class DeadLock implements Runnable{
  private boolean flag;
  DeadLock(boolean flag){
    this.flag=flag;
  }
  public void run() {
    if(flag) {
      while(true) {
        synchronized(MyLock.obj1) {
          System.out.println(Thread.currentThread().getName()+"----if获得obj1锁");
          synchronized(MyLock.obj2) {
            System.out.println(Thread.currentThread().getName()+"----if获得obj2锁");
          }
        }
      }
    } else {
      while(true){
        synchronized(MyLock.obj2) {
          System.out.println(Thread.currentThread().getName()+"----否则获得obj2锁");
          synchronized(MyLock.obj1) {
            System.out.println(Thread.currentThread().getName()+"----否则获得obj1锁");
          }
        }
      }
    }
  }
}

运行结果

image.png

  • jstack分析
    image.png

把打印信息拉到最后可以发现

image.png

1.2.5 jmap

(1)生成堆转储快照

The jmap command prints shared object memory maps or heap memory details of a
specified process, core file, or remote debug server.

(2)打印出堆内存相关信息

-XX:+PrintFlagsFinal -Xms300M -Xmx300M
jmap -heap PID

image.png

(3)dump出堆内存相关信息
jmap -dump:format=b,file=heap.hprof PID

jmap -dump:format=b,file=heap.hprof 44808

image.png

(4)要是在发生堆内存溢出的时候,能自动dump出该文件就好了
一般在开发中,JVM参数可以加上下面两句,这样内存溢出时,会自动dump出该文件
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.hprof

设置堆内存大小: -Xms20M -Xmx20M
启动,然后访问localhost:9090/heap,使得堆内存溢出

(5)关于dump下来的文件
一般dump下来的文件可以结合工具来分析,这块后面再说

1.3 常用工具

参数也了解了,命令也知道了,关键是用起来不是很方便,要是有图形化的界面就好了。
一定会有好事之者来做这件事情。

1.3.1 jconsole

JConsole工具是JDK自带的可视化监控工具。查看java应用程序的运行概况、监控堆信息、永久区使用情况、类加载情况等。

命令行中输入:jconsole

1.3.2 jvisualvm

1.3.2.1 监控本地Java进程

可以监控本地的java进程的CPU,类,线程等

1.3.2.2 监控远端Java进程

比如监控远端tomcat,演示部署在阿里云服务器上的tomcat

  • 在visualvm中选中“远程”,右击“添加”
  • 主机名上写服务器的ip地址,比如39.100.39.63,然后点击“确定”
  • 右击该主机“39.100.39.63”,添加“JMX”[也就是通过JMX技术具体监控远端服务器哪个Java进程]
  • 要想让服务器上的tomcat被连接,需要改一下 bin/catalina.sh 这个文件

注意下面的8998不要和服务器上其他端口冲突

image.png

  • 在 ../conf 文件中添加两个文件jmxremote.access和jmxremote.password

jmxremote.access 文件

guest readonly
manager readwrite

jmxremote.password 文件

guest guest 
manager manager
授予权限 : chmod 600 *jmxremot*
  • 将连接服务器地址改为公网ip地址
hostname -i #查看输出情况
  172.26.225.240 172.17.0.1
vim /etc/hosts
  172.26.255.240 39.100.39.63
# 设置上述端口对应的阿里云安全策略和防火墙策略
# 启动tomcat,来到bin目录
./startup.sh
  • 查看tomcat启动日志以及端口监听
tail -f ../logs/catalina.out
lsof -i tcp:8080
  • 查看8998监听情况,可以发现多开了几个端口
lsof -i:8998 得到PID
netstat -antup | grep PID
  • 在刚才的JMX中输入8998端口,并且输入用户名和密码则登录成功
端口:8998
用户名:manager
密码:manager

1.3.3 Arthas

github :https://github.com/alibaba/arthas

Arthas allows developers to troubleshoot production issues for Java
applications without modifying code or restarting servers.

Arthas 是Alibaba开源的Java诊断工具,采用命令行交互模式,是排查jvm相关问题的利器。
image.png

1.3.3.1 下载安装

curl -O https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar
# 然后可以选择一个Java进程

Print usage

java -jar arthas-boot.jar -h

1.3.3.2 常用命令

具体每个命令怎么使用,大家可以自己查阅资料

version:查看arthas版本号
help:查看命名帮助信息
cls:清空屏幕
session:查看当前会话信息
quit:退出arthas客户端

dashboard:当前进程的实时数据面板
thread:当前JVM的线程堆栈信息
jvm:查看当前JVM的信息
sysprop:查看JVM的系统属性

sc:查看JVM已经加载的类信息
dump:dump已经加载类的byte code到特定目录
jad:反编译指定已加载类的源码

monitor:方法执行监控
watch:方法执行数据观测
trace:方法内部调用路径,并输出方法路径上的每个节点上耗时
stack:输出当前方法被调用的调用路径
……

1.3.4 MAT

Java堆分析器,用于查找内存泄漏
Heap Dump,称为堆转储文件,是Java进程在某个时间内的快照。
它在触发快照的时候保存了很多信息:Java对象和类信息。
通常在写Heap Dump文件前会触发一次Full GC。
下载地址 :https://www.eclipse.org/mat/downloads.php

1.3.4.1 Dump的信息

  • All Objects
    Class, fields, primitive values and references
  • All Classes
    Classloader, name, super class, static fields
  • Garbage Collection Roots
    Objects defined to be reachable by the JVM
  • Thread Stacks and Local Variables
    The call-stacks of threads at the moment of the snapshot, and per-frame information about local
    objects

1.3.4.2 获取Dump文件

  • 手动
jmap -dump:format=b,file=heap.hprof 44808
  • 自动
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.hprof

1.3.4.3 使用

  • Histogram

Histogram可以列出内存中的对象,对象的个数及其大小

Class Name:类名称,java类名
Objects:类的对象的数量,这个对象被创建了多少个
Shallow Heap:一个对象内存的消耗大小,不包含对其他对象的引用
Retained Heap:是shallow Heap的总和,即该对象被GC之后所能回收到内存的总和
右击类名—>List Objects—>with incoming references—>列出该类的实例
右击Java对象名—>Merge Shortest Paths to GC Roots—>exclude all …—>找到GC Root以及原因
  • Leak Suspects

查找并分析内存泄漏的可能原因

Reports—>Leak Suspects—>Details
  • Top Consumers

列出大对象

1.3.4 GC日志分析工具

要想分析日志的信息,得先拿到GC日志文件才行,所以得先配置一下,根据前面参数的学习,下面的配置很容易看懂

-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps
-Xloggc:$CATALINA_HOME/logs/gc.log

2. 性能优化及总结

2.1 重新认知JVM

之前我们画过一张图,是从Class文件到类装载器,再到运行时数据区的过程。
现在咱们把这张图不妨丰富完善一下,展示了JVM的大体物理结构图。

image.png

执行引擎:用于执行JVM字节码指令
主要由两种实现方式:
(1)将输入的字节码指令在加载时或执行时翻译成另外一种虚拟机指令;
(2)将输入的字节码指令在加载时或执行时翻译成宿主主机本地CPU的指令集。这两种方式对应着字节码的解释执行和即时编译。

2.2 GC优化

内存被使用了之后,难免会有不够用或者达到设定值的时候,就需要对内存空间进行垃圾回收。

2.2.1 垃圾收集发生的时机

GC是由JVM自动完成的,根据JVM系统环境而定,所以时机是不确定的。当然,我们可以手动进行垃圾回收,比如调用System.gc()方法通知JVM进行一次垃圾回收,但是具体什么时刻运行也无法控制。也就是说System.gc()只是通知要回收,什么时候回收由JVM决定。
但是不建议手动调用该方法,因为消耗的资源比较大
一般以下几种情况会发生垃圾回收

(1)当Eden区或者S区不够用了
(2)老年代空间不够用了
(3)方法区空间不够用了
(4)System.gc()

2.2.2 实验环境准备

我的本地机器使用的是jdk1.8和tomcat8.5,大家也可以使用linux上的tomcat,然后把gc日志下载下来即可。

2.2.3 GC日志文件

image.png

要想分析日志的信息,得先拿到GC日志文件才行,所以得先配置一下,之前也看过这些参数

-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps
-Xloggc:gc.log

比如打开windows中的catalina.bat,在第一行加上

set JAVA_OPTS=%JAVA_OPTS% -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:gc.log

这样使用startup.bat启动tomcat的时候就能够在当前目录下拿到gc.log文件
可以看到默认使用的是Parallel GC

2.2.3.1 Parallel GC日志

【吞吐量优先】

2019-06-10T23:21:53.305+0800: 1.303:
[GC (Allocation Failure) [PSYoungGen: 65536K[Young区回收前]->10748K[Young区回
收后](76288K[Young区总大小])] 65536K[整个堆回收前]->15039K[整个堆回收后]
(251392K[整个堆总大小]), 0.0113277 secs] [Times: user=0.00 sys=0.00,
real=0.01 secs]

注意 :如果回收的差值中间有出入,说明这部分空间是Old区释放出来的
image.png

2.2.3.2 CMS日志

【停顿时间优先】
参数设置:-XX:+UseConcMarkSweepGC -Xloggc:cms-gc.log

重启tomcat获取gc日志,这里的日志格式和上面差不多,不作分析。

2.2.3.3 G1日志

初始标记(Initial Marking)                                  标记一下GC Roots能够关联的对象,并且修改TAMS的值,需要暂停用户线程
并发标记(Concurrent Marking)                         从GC Roots进行可达性分析,找出存活的对象,与用户线程并发执行
最终标记(Final Marking)                                    修正在并发标记阶段因为用户程序的并发执行导致变动的数据,需暂停用户线程
筛选回收(Live Data Counting and Evacuation) 对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间制定
回收计划

image.png

【停顿时间优先】

参数设置:-XX:+UseG1GC -Xloggc:g1-gc.log

理解G1日志格式:https://blogs.oracle.com/poonam/understanding-g1-gc-logs

-XX:+UseG1GC # 使用了G1垃圾收集器

# 什么时候发生的GC,相对的时间刻,GC发生的区域young,总共花费的时间,0.00478s,
# It is a stop-the-world activity and all
# the application threads are stopped at a safepoint during this time.
2019-12-18T16:06:46.508+0800: 0.458: [GC pause (G1 Evacuation Pause)
(young), 0.0047804 secs]

# 多少个垃圾回收线程,并行的时间
[Parallel Time: 3.0 ms, GC Workers: 4]

# GC线程开始相对于上面的0.458的时间刻
[GC Worker Start (ms): Min: 458.5, Avg: 458.5, Max: 458.5, Diff: 0.0]

# This gives us the time spent by each worker thread scanning the roots
# (globals, registers, thread stacks and VM data structures).
[Ext Root Scanning (ms): Min: 0.2, Avg: 0.4, Max: 0.7, Diff: 0.5, Sum: 1.7]

# Update RS gives us the time each thread spent in updating the Remembered Sets.
[Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
...

# 主要是Eden区变大了,进行了调整
[Eden: 14.0M(14.0M)->0.0B(16.0M) Survivors: 0.0B->2048.0K Heap:
14.0M(256.0M)->3752.5K(256.0M)]

image.png

2.2.4 GC日志文件分析工具

2.2.4.1 gceasy

官网 :https://gceasy.io
可以比较不同的垃圾收集器的吞吐量和停顿时间
比如打开cms-gc.log和g1-gc.log

image.png

image.png

2.2.4.2 GCViewer

image.png

2.2.5 G1调优与最佳指南

2.2.5.1 调优

是否选用G1垃圾收集器的判断依据
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/G1.html#use_cases

(1)50%以上的堆被存活对象占用
(2)对象分配和晋升的速度变化非常大
(3)垃圾回收时间比较长

思考why :https://blogs.oracle.com/poonam/increased-heap-usage-with-g1-gc

  • 使用G1GC垃圾收集器: -XX:+UseG1GC

修改配置参数,获取到gc日志,使用GCViewer分析吞吐量和响应时间

Throughput 吞吐量
Min Pause 最少停顿时间
Max Pause 最大停顿时间
Avg Pause 平均停顿时间
GC count GC次数
Throughput Min Pause Max Pause Avg Pause GC count
99.16% 0.00016s 0.0137s 0.00559s 12
  • 调整内存大小再获取gc日志分析
-XX:MetaspaceSize=100M -Xms300M -Xmx300M

比如设置堆内存的大小,获取到gc日志,使用GCViewer分析吞吐量和响应时间

Throughput Min Pause Max Pause Avg Pause GC count
98.89% 0.00021s 0.01531s 0.00538s 12
  • 调整最大停顿时间
-XX:MaxGCPauseMillis=200 设置最大GC停顿时间指标

比如设置最大停顿时间,获取到gc日志,使用GCViewer分析吞吐量和响应时间

Throughput Min Pause Max Pause Avg Pause GC count
98.96% 0.00015s 0.01737s 0.00574s 12
  • 启动并发GC时堆内存占用百分比
-XX:InitiatingHeapOccupancyPercent=45
G1用它来触发并发GC周期,基于整个堆的使用率,而不只是某一代内存的使用比例。值为 0 则表示**一直执行GC循环**. 默认值为 45 (例如, 全部的 45% 或者使
用了45%).

比如设置该百分比参数,获取到gc日志,使用GCViewer分析吞吐量和响应时间

Throughput Min Pause Max Pause Avg Pause GC count
98.11% 0.00406s 0.00532s 0.00469s 12

2.2.5.2 最佳指南

官网建议:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.ht ml#recommendations

  • 不要手动设置新生代和老年代的大小,只要设置整个堆的大小
G1收集器在运行过程中,会自己调整新生代和老年代的大小
其实是通过adapt代的大小来调整对象晋升的速度和年龄,从而达到为收集器设置的暂停时间目标
如果手动设置了大小就意味着放弃了G1的自动调优
  • 不断调优暂停时间目标
一般情况下这个值设置到100ms或者200ms都是可以的(不同情况下会不一样),但如果设置成50ms就
不太合理。暂停时间设置的太短,就会导致出现G1跟不上垃圾产生的速度。最终退化成Full GC。所以
对这个参数的调优是一个持续的过程,逐步调整到最佳状态。暂停时间只是一个目标,并不能总是得到
满足。
  • 使用-XX:ConcGCThreads=n来增加标记线程的数量
IHOP如果阀值设置过高,可能会遇到转移失败的风险,比如对象进行转移时空间不足。如果阀值设置过
低,就会使标记周期运行过于频繁,并且有可能混合收集期回收不到空间。
IHOP值如果设置合理,但是在并发周期时间过长时,可以尝试增加并发线程数,调高ConcGCThreads。
  • MixedGC调优
-XX:InitiatingHeapOccupancyPercent
-XX:G1MixedGCLiveThresholdPercent
-XX:G1MixedGCCountTarger
-XX:G1OldCSetRegionThresholdPercent
  • 适当增加堆内存大小

2.3 高并发场景分析

以每秒3000笔订单为例

image.png

2.4 JVM性能优化指南

image.png

2.5 常见问题解答

  • 内存泄漏与内存溢出的区别
    内存泄漏是指不再使用的对象无法得到及时的回收,持续占用内存空间,从而造成内存空间的浪费
    内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。
    内存泄漏很容易导致内存溢出,但内存溢出不一定是内存泄漏导致的
  • young gc会有 stop the world吗?
    不管什么 GC,都会发送 stop the world,区别是发生的时间长短。而这个时间跟垃圾收集器又有关系,Serial、PartNew、Parallel Scavenge 收集器无论是串行还是并行,都会挂起用户线程,而 CMS和 G1 在并发标记时,是不会挂起用户线程的,但其它时候一样会挂起用户线程,stop the world 的时
    间相对来说就小很多了
  • Major GC和Full GC的区别
    Major Gc 在很多参考资料中是等价于Full GC的,我们也可以发现很多性能监测工具中只有 Minor GC和 Full GC。一般情况下,一次 Full GC 将会对年轻代、老年代、元空间以及堆外内存进行垃圾回收。触发Full GC 的原因有很多:当年轻代晋升到老年代的对象大小,并比目前老年代剩余的空间大小还要大
    时,会触发 Full GC;当老年代的空间使用率超过某阈值时,会触发 Full GC;当元空间不足时(JDK1.7永久代不足),也会触发 Full GC;当调用 System.gc() 也会安排一次 Full GC。
  • G1与CMS的区别
    CMS 主要集中在老年代的回收,而 G1 集中在分代回收,包括了年轻代的 Young GC 以及老年代的 Major GC;G1 使用了Region方式对堆内存进行了划分,且基于标记整理算法实现,整体减少了垃圾碎片的产生;在初始化标记阶段,搜索可达对象使用到的 Card Table,其实现方式不一样。
  • 什么是直接内存
    Java的NIO库允许Java程序使用直接内存。直接内存是在java堆外的、直接向系统申请的内存空间。通常访问直接内存的速度会优于Java堆。因此出于性能的考虑,读写频繁的场合可能会考虑使用直接内存。由于直接内存在java堆外,因此它的大小不会直接受限于Xmx指定的最大堆大小,但是系统内存是有限的,Java堆和直接内存的总和依然受限于操作系统能给出的最大内存。
  • 垃圾判断的方式
    引用计数法:指的是如果某个地方引用了这个对象就+1,如果失效了就-1,当为0就会回收但是JVM没有用这种方式,因为无法判定相互循环引用(A引用B,B引用A)的情况
    引用链法: 通过一种GC ROOT的对象(方法区中静态变量引用的对象等-static变量)来判断,如果有一条链能够到达GC ROOT就说明,不能到达GC ROOT就说明可以回收
  • 不可达的对象一定要被回收吗?
    即使在可达性分析法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于缓刑阶段,要真正宣告一个对象死亡,至少要经历两次标记过程;可达性分析法中不可达的对象被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize方法。当对象没有覆盖finalize方法,或finalize方法已经被虚拟机调用过时,虚拟机将这两种情况视为没有必要执行。
    被判定为需要执行的对象将会被放在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个对象建立关联,否则就会被真的回收。
  • 方法区中的无用类回收
    方法区主要回收的是无用的类,那么如何判断一个类是无用的类的呢?
    判定一个常量是否是废弃常量比较简单,而要判定一个类是否是无用的类的条件则相对苛刻许多。
    类需要同时满足下面 3 个条件才能算是无用的类
  1. 该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例
  2. 加载该类的ClassLoader已经被回收。
  3. 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
    虚拟机可以对满足上述 3 个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样不使用了就会必然被回收。
  • 不同的引用
    JDK1.2以后,Java对引用进行了扩充:强引用、软引用、弱引用和虚引用
  • 为什么要区分新生代和老年代?
    当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将java堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法
    比如在新生代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择标记-清除标记-整理算法进行垃圾收集。

作者:周鑫磊

来源:https://www.sparksys.top/archives/10


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (1)