文章来源:https://blog.csdn.net/a2572371/article/details/78202874
一.根据Java虚拟机规范,JVM将内存划分为:
1.New(年轻代)
2.Tenured(年老代)
3.永久代(Perm)(备注:jdk1.8之后 改为元本地元空间)
其中,New和Tenured属于堆内存。-xmx:指定jvm堆内存最大值,-xms:jvm堆初始化值。
永久代,Perm(非堆)不属于堆内存,有虚拟机直接分配,但可以通过-XX:PermSize -XX:MaxPermSize等参数调整其大小。
年轻代(New):年轻代用来存放JVM刚分配的Java对象
年老代(Tenured):年轻代中经过垃圾回收没有回收掉的对象将被Copy到年老代
永久代(Perm):永久代存放Class、Method元信息,其大小跟项目的规模、类、方法的量有关,一般设置为128M就足够,设置原则是预留30%的空间。
New又分为几个部分:
Eden:用来存放JVM刚分配的对象
Survivor1
Survivro2:两个Survivor空间一样大,当Eden中的对象经过垃圾回收没有被回收掉时,会在两个Survivor之间来回Copy,当满足某个条件,比如Copy次数,就会被Copy到 Tenured。显然,Survivor只是增加了对象在年轻代中的逗留时间,增加了被垃圾回收的可能性。
二.垃圾回收算法
垃圾回收算法可以分为三类,都基于标记-清除(复制)算法:
1.Serial算法(单线程)
2.并行算法
3.并发算法
JVM会根据机器的硬件配置对每个内存代选择适合的回收算法,比如,如果机器多于1个核,会对年轻代选择并行算法。
并行算法是用多线程进行垃圾回收,回收期间会暂停程序的执行,而并发算法,也是多线程回收,但期间不停止应用执行
并发算法适用于交互性高的一些程序。经过观察,并发算法会减少年轻代的大小,其实就是使用了一个大的年老代,这反过来跟并行算法相比吞吐量相对较低
调优:
(1)针对JVM堆的设置,一般可以通过-Xms -Xmx限定其最小、最大值,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,我们通常把最大、最小设置为相 同的值
(2)年轻代和年老代将根据默认的比例(1:2)分配堆内存,可以通过调整二者之间的比率NewRadio来调整二者之间的大小,也可以针对回收代,比如年轻代,通过 -XX:newSize -XX:MaxNewSize来设置其绝对大小。同样,为了防止年轻代的堆收缩,我们通常会把-XX:newSize -XX:MaxNewSize设置为同样大小
(3)年轻代和年老代设置多大才算合理?这个我问题毫无疑问是没有答案的,否则也就不会有调优。我们观察一下二者大小变化有哪些影响
更大的年轻代必然导致更小的年老代,大的年轻代会延长普通GC的周期,但会增加每次GC的时间;小的年老代会导致更频繁的Full GC
更小的年轻代必然导致更大年老代,小的年轻代会导致普通GC很频繁,但每次的GC时间会更短;大的年老代会减少Full GC的频率
(4)-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${目录} --》参数表示当JVM发生OOM时,自动生成DUMP文件,进行排查问题比较关键
jvm设置
java -Xmx355m -Xms355m -Xmn200m -Xss128k
-Xmx3550m:设置JVM最大内存
-Xms3550m:设置JVM初始内存。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn2g:设置年轻代。整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对
系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减
小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:MaxPermSize=16m:设置持久代大小为16m。
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将
此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
- 堆设置
- -Xms:初始堆大小
- -Xmx:最大堆大小
- -XX:NewSize=n:设置年轻代大小
- -XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
- -XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
- -XX:MaxPermSize=n:设置持久代大小
- -Xss128k;设置每个线程的堆栈大小
- 垃圾收集器设置
- -XX:+UseSerialGC:设置串行收集器
- -XX:+UseParallelGC:设置并行收集器
- -XX:+UseParalledlOldGC:设置并行年老代收集器
- -XX:+UseConcMarkSweepGC:设置并发收集器(CMS)
- G1收集器
- 垃圾回收统计信息(GC日志查看)
- -XX:+PrintGC 输出GC日志
- -XX:+PrintGCDetails 输出GC的详细日志
- -XX:+PrintGCTimeStamps 输出GC的时间戳
- -Xloggc:filename 将GC日志输出到文件
- 并行收集器设置
- -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
- -XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
- -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
- 并发收集器设置
- -XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
- -XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。
Minor GC ,Full GC 触发条件
Minor GC触发条件:当Eden区满时,触发Minor GC。
Full GC触发条件:
(1)调用System.gc时,系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
(3)方法去空间不足
(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存
(5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小
重点:
1. jvm调优思路
jvm调优其实更多的是对GC的优化,尤其是尽量减少full GC。
大多数情况下,对象在Eden区分配,当Eden区没有足够空间进行分配时,虚拟机将进行一次Minor GC ,可能有99%的对象被标记为垃圾被回收,剩余存活的对象会进入为空的survivor,下一次Eden区满了之后,又会触发minor gc,把Eden区和survivor区垃圾对象回收,把剩余存活的对象一次性挪动到另外一块为空的to区,因为新生代的对象都是朝生夕死的,存活时间很短,所以JVM默认的8:1:1的比例是很合适的,让eden区尽量的大,survivor区够用即可。
Minor GC/Young GC:指发生新生代的的垃圾收集动作,Minor GC非常频繁,回收速度一般也比较快。
Major GC/Full GC:一般会回收老年代 ,年轻代,方法区的垃圾,Major GC的速度一般会比Minor GC的慢 10倍以上。
Eden与Survivor区默认8:1:1
明白了上边的对象流转过程,我们可以在这个过程中做一些手脚,来进行jvm调优!
2 jvm调优方案
①:设置大对象直接进入老年代! 大对象就是需要大量内存空间的对象(比如数组、字符串) ,通过jvm参数-XX:PretenureSizeThreshold 可以设置大对象的大小,如果对象超过设置大小会直接进入老年代,不会进入年轻代,这个参数只在 Serial 和ParNew两个收集器下有效。
具体操作:-XX:PretenureSizeThreshold=1000000 (单位是字节) -XX:+UseSerialGC
使用场景:当我们可以确定系统中的对象大部分为大对象,且短期内不会被垃圾回收,就可以根据对象大小设置jvm参数,让这些大对象直接进入老年代,省去了对象在新生代流转的过程,节省了Eden区的空间,因为大对象最终总会进入老年代的,还不如提前让出Eden空间,让他处理更多的小对象,提升系统性能!
②:设置长期存活的对象提前进入老年代! 新生代的对象每熬过一次Minor GC ,其年龄就会+1,默认15岁,也就是流转15次就会进入老年代。当我们的系统中大概有大部分(80%)的对象都会经过15次Minor GC 进入老年代,我们可以通过设置-XX:MaxTenuringThreshold来调整进入老年代需要的年龄阈值。比如设置年龄为8即可进入老年代,这样那些长期存活的对象,就可以尽早的进入老年代,减少对象在新生代的流转次数,提升了系统性能!
③:根据survivor区的动态年龄判断机制,合理设置新生代大小 。一般超过survivor区大小的60%会发生动态年龄判断机制,此时把最老的对象放进老年代。可以适当增加survivor区的大小避免Full GC!动态年龄判断的机制作用其实是希望那些可能是长期存活的对象,尽早进入老年代,避免多次复制操作而降低效率。
其他情况调优:( Java heap space)
针对大部分情况,通常只需要通过 -Xmx 参数调高 JVM 堆内存空间即可。如果仍然没有解决,可以参考以下情况做进一步处理:
1、如果是超大对象,可以检查其合理性,比如是否一次性查询了数据库全部结果,而没有做结果数限制。
2、如果是业务峰值压力,可以考虑添加机器资源,或者做限流降级。
3、如果是内存泄漏,需要找到持有的对象,修改代码设计,比如关闭没有释放的连接。
PermGen space
该错误表示永久代 (Permanent Generation) 已用满, 通常是因为加载的 class 数目太多或体积太大
根据 Permgen space 报错的时机,可以采用不同的解决方案,如下所示:
1、程序启动报错,修改 -XX:MaxPermSize 启动参数,调大永久代空间。
2、应用重新部署时报错,很可能是没有应用没有重启,导致加载了多份 class 信息,只需重启 JVM 即可解决。
3、运行时报错,应用程序可能会动态创建大量 class,而这些 class 的生命周期很短暂,但是 JVM 默认不会卸载 class,可以设置 -XX:+CMSClassUnloadingEnabled 和 -XX:+UseConcMarkSweepGC这两个参数允许 JVM 卸载 class。
4、如果上述方法无法解决,可以通过 jmap 命令 dump 内存对象 jmap-dump:format=b,file=dump.hprof ,然后利用 Eclipse MAT https://www.eclipse.org/mat 功能逐一分析开销最大的 classloader 和重复 class。
订单的秒杀模块jvm调优案例
架构如下:
对亿级电商平台的调优中,首先要对自己的系统有足够的了解。根据以上架构:
①:如果平台日活用户为500w,那大部分的付费转化率为10%,也就是每日50w单左右。
②:如果50w单是在平时非促销的时候,下订单操作通过负载均衡打到服务器上,也就每秒几单、十几单的样子,服务器可以抗住。但如果要搞促销,50w单要在几分钟内产生,那么每秒钟就高达1000多单的交易,如果不合理设置jvm参数,就可能会频繁发生Full GC!
③:假设有订单三台服务器,每秒1000单通过负载均衡到三台服务器,每台服务器每秒处理300单左右!每个订单对象的大小是由order类中的字段来决定的,假设每个订单对象有几十个字段,撑死1kb,每秒300kb对象生成,订单接口中肯定不止一个订单对象,还有库存、优惠券、积分等对象,所以300kb放大20倍,也就是6MB,同时还可能有别的操作,比如订单查询等,再放大10倍,也就是60MB,因为要保证请求速度,所以这60MB对象1s后都会成为垃圾。
④:此时每秒60M对象进入堆内存。假如服务器内存是8G,给操作系统分配4-5G,剩下的分给JVM,因为 新生代:老年代 = 1:2,则新生代拿到1G,老年代2G,元空间512Mb,栈1M,jvm参数设置 如图所示!对象会首先进入Eden,800/60 ≈ 14秒 ,也即14秒时Eden区被放满,发生Minor GC!
⑤:Minor GC会进行stw,前13秒的对象直接被gc掉。然而由于gc时的stw,用户线程无法执行,此时可能第14秒创建的对象会没有使用完,就会一直被引用着,GC完毕后,第14秒创建的对象由于被引用而存活了下来,此时存活的对象会进入survivor区。由于survivor区存在动态年龄判断机制:如果Survivor区中的这批对象总大小大于Survivor区域内存大小的50%,那么>=这批年龄最大值的对象,都会被放入老年代。 该组对象大小>50MB,年龄都是1,所以此时第14秒这批对象都会直接进入老年代。也就是每14秒60M对象进入老年代!导致几分钟一次GC!
⑥:由于上述原因就是由survivor区存在动态年龄判断机制导致的,所以我们可以通过调整新生代大小来避免,对象进入老年代!
把年轻代分配2G,则survivor区为200M,60M的对象进来不至于触发动态年龄判断机制,一次Minor GC就杀死了所有对象,几乎没有对象进入老年代,解决了Fulle GC频繁的问题!
参考地址:JVM调优思路、订单秒杀jvm调优案例_51CTO博客_jvm调优
JVM工具监控和分析
使用各种JVM工具,查看当前日志,分析当前JVM参数设置,并且分析当前堆内存快照和gc日志,根据实际的各区域内存划分和GC执行时间,觉得是否进行优化。
举一个例子: 系统崩溃前的一些现象:
- 每次垃圾回收的时间越来越长,由之前的10ms延长到50ms左右,FullGC的时间也有之前的0.5s延长到4、5s
- FullGC的次数越来越多,最频繁时隔不到1分钟就进行一次FullGC
- 年老代的内存越来越大并且每次FullGC后年老代没有内存被释放
之后系统会无法响应新的请求,逐渐到达OutOfMemoryError的临界值,这个时候就需要分析JVM内存快照dump。
1、第一步下载插件
本文章使用jdk自带的测试工具Visual VM(C:\Program Files\Java\jdk1.8.0_51\bin目录下)
打开后在工具菜单中安装visual GC 插件(该插件能显示年轻代,年老代,持久代,垃圾回收次数,垃圾回收时间)
插件下载地址:VisualVM: Plugins Centers
安装后重新打开软件
远程连接服务器使用方案:使用Java VisualVM监控远程JVM(远程服务器为linux配置)_紫漪的博客-CSDN博客_visualvm远程监控jvm
2、IDEA 新建一个main方法类,创建两个线程并死循环
new Thread() {
public void run() {
while(true){
String test = new String("test");
System.out.println(test);
}
}
}.start();
new Thread() {
public void run() {
while(true){
String test1 = new String("test1");
System.out.println(test1);
}
}
}.start();
3、软件分析
main方法执行后
(1)、监控,,可以看到堆和持久代使用情况
(2)、线程分析
main方法中的两个线程,可以看到两个线程实时上下文切换
3、visual GC分析
年轻代(eden, Survivor1, Survivor2),年老代,持久代,垃圾回收次数,垃圾回收时间
4、Profiler 中的CPU和内存具体查看执行方法
CPU查看具体执行方法
内存具体查看
重点1:如果满足下面的指标,则一般不需要进行GC:
- Minor GC执行时间不到50ms;
- Minor GC执行不频繁,约10秒一次;
- Full GC执行时间不到1s;
- Full GC执行频率不算频繁,不低于10分钟1次;
其他博客参考: java之JVM调优
重点2:调优具体使用:
- 在linux中启动jar 在命令后加入相关参数;
- IDEA中在VM options中设置参数,比如:-Xmx10m -Xms5m -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps,
在控制台可以查看到打印出来的日志;
3.817: [GC (Allocation Failure) [PSYoungGen: 1056K->32K(2048K)] 2187K->1163K(6144K), 0.0138724 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
具体分析解答如下:GC(Allocation Failure)引发的一些JVM知识点梳理_淡定一生2333的博客-CSDN博客_allocation failure
重点3:常见的垃圾收集器有Serial GC、ParNew GC、CMS GC、Parallel GC、G1 GC区别:
地址如下:Java 常见的垃圾收集器有Serial GC、ParNew GC、CMS GC、Parallel GC、G1 GC_测试-CSDN博客_java parnewgc
串行垃圾回收器--》Serial 收集器
单线程的垃圾回收器,在垃圾回收时,需要其它线程暂停,等待垃圾回收完毕。
开启串行垃圾回收器的参数:-XX:+UseSerialGC = serial + serialOld
-
serial是工作在新生代,采用的是复制算法;
-
serialOld是工作在老年代,采用的是标记整理算法
缺点:STW时间较长(指的是Gc事件发生过程中,会产生应用程序的停顿)
优点:简单又高效,没有线程交互的消耗,收集效率高。
吞吐量优先垃圾回收器--》Parallel收集器
多线程的垃圾回收器,注重的是单位时间内垃圾回收的STW时间最短。需要多核CPU支持,是和在服务器中 使用。
像 Serial 收集器一样,Parallel 收集器也使用“stop the world”方法。这意味着,当垃圾收集器运行时,应用程序线程会停止。但是不同的是,Parallel 收集器运行时有多个线程执行垃圾收集操作。这种类型的垃圾收集器适用于在多线程和多处理器环境中运行中到大型数据集的应用程序
这是 JVM 中的默认垃圾收集器,也被称为吞吐量收集器
开启吞吐量优先垃圾回收器的参数:-XX:+Use Parallel GC ~ -XX:+Use Parallel Old GC
(Parallel:并行的)在JDK8是默认开启了这两个开关。当你手动开启其中一个开关,另一个也会默认开启。
- ParallelGC 是工作在新生代,采用的是复制算法;
- ParallelOldGC 是工作在老年代,采用的是标记整理算法。
优点:单位STW时间较短
缺点:垃圾回收时,CPU占用率过高
响应时间优先垃圾回收器--》CMS GC和ParNewGC
多线程的的垃圾回收器,注重的是尽可能让单次垃圾回收中STW的时间最短,在垃圾回收时,不需要其它线程暂停,可以和其它用户线程并发(concurrent)执行是多个线程和垃圾回收线程抢占CPU。
开启响应时间优先垃圾回收器的参数:-XX: +UseConcMarkSweepGC~-XX: +UseParNewGC~ Serial0ld
- CMS GC是工作在老年代的,使用标记清除算法的GC。
- ParNew GC是工作在新生代的,使用复制算法的GC
优点:并发收集、低停顿
缺点:垃圾回收占用了一整个线程,整个程序的吞吐量降低。会产生内存碎片。
G1(Garbage First)收集器
G1 垃圾收集器旨在替代 GMS。G1 垃圾收集器具备并行、并发以及增量压缩,且暂停时间较短。与 CMS 收集器使用的内存布局不同,G1 收集器将堆内存划分为大小相同的区域,通过多个线程触发全局标记阶段。标记阶段完成后,G1 知道哪个区域可能大部分是空的,并首选该区域作为清除/删除阶段。
在 G1 收集器中,一个对象如果大小超过半个区域容量会被认为是一个“大对象” 。这些对象被放置在老年代中,在一个被称为“humongous region”的区域中。
G1 (Garbage-First) 是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 同时注重吞吐量(Throughput) 和低延迟(Low latency) .
整体上是标记+整理算法,两个区域之间是使用复制算法
超大堆内存,会将堆划分为多个大小相等的区域Region(每个区都有自己新生代和老年代)
开启G1垃圾回收器的参数:- XX: +UseG1GC 在JDK9之前需要手动启用G1回收
初始标记:在Young GC时会进行GC Root的初始标记
并发标记:老年代占用堆空间比例达到阈值时,进行并发标记(不会STW),
JVM参数决定: -XX:InitiatingHeapOccupancyPercent = percent (默认45%),最终标记:对E、S、O区域进行全面垃圾回收,会STW
筛选回收:
具体的回收步骤:
将E区和S区的无用对象进行回收,有用对象进行复制算法到一个新的S区。
将符合晋升阈值的对象加入到老年代中。
当现有的O区中内存已满,就会根据暂停时间,在暂停时间内,有选择的将O区中的对象复制到新的O区中。(优先回收能释放出更多内存的区域,所以叫做Garbage First)
Serial Old 垃圾回收器
Serial Old 和Serial的工作模式一样,唯一区别是它采用了标记-整理垃圾回收算法,主要是为老年代垃圾回收服务。
ParNew Old 垃圾回收器
ParNew Old 和ParNew 的工作模式一样,唯一区别是它采用了标记-整理垃圾回收算法,主要是为老年代垃圾回收服务。
新生代和老年代分类: ·
新生代收集器: Serial、ParNew、Parallel Scavenge
老年代收集器: Serial Old、Parallel Old、CMS
整堆收集器:G1
算法:
标记复制算法:Serial、ParNew、Parallel Scavenge、G1
标记清除算法:CMS
标记整理算法:Serial Old、Parallel Old、G1
重点4:内存溢出和内存泄漏的区别?
内存泄漏memory leak :指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
内存溢出 out of memory :是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory
内存泄漏可以分为4类:
- 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
- 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
- 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。
- 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。
两者关系:
1、内存泄漏的堆积最终会导致内存溢出
2、内存溢出就是你要的内存空间超过了系统实际分配给你的空间,此时系统相当于没法满足你的需求,就会报内存溢出的错误。
3、内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。就相当于你租了个带钥匙的柜子,你存完东西之后把柜子锁上之后,把钥匙丢了或者没有将钥匙还回去,那么结果就是这个柜子将无法供给任何人使用,也无法被垃圾回收器回收,因为找不到他的任何信息。
内存泄露原因:
1、创建和应用生命周期一样的单例对象
2、创建匿名内部类的静态对象
3、未关闭资源
4、长时间存在的集合容器中创建生命周期短的对象
内存溢出原因:
1、启动参数内存值设定的过小
2、内存中加载的数据量过于庞大
3、代码中存在死循环或者递归中创建对象
避免内存溢出
1、尽早释放无用内存
2、处理字符串尽可能使用StringBuffer,因为每创建一个String占一个独立内存
3、避免死循环或者递归中创建对象
4、修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
性能调优
- 适当增加内存,根据业务背景选择垃圾回收器
- 优化代码,控制内存使用
- 增加机器,分散节点压力
- 合理设置线程池线程数量
- 使用中间件提高程序效率,比如缓存、消息队列等
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/106228.html