程序中 设置jvm 参数
在撰写本文时(2020年3月),围绕垃圾收集和内存,您可以将600多个参数传递给JVM。 如果您包括其他方面,则JVM参数总数将很容易超过1000个。 arguments任何人都无法消化和理解太多的论据。 在本文中,我们重点介绍了您可能会发现有用的七个重要JVM参数。
还请参见: Java 14 –“ NullPointerExceptions是一项重大改进”
1. -Xmx和-XX:MaxMetaspaceSize
-Xmx可能是最重要的JVM参数。 -Xmx定义要分配给应用程序的最大堆大小。 (要了解JVM中的不同内存区域,您可以观看此短片)。 您可以这样定义应用程序的堆大小:
-Xmx2g
堆大小在确定您的以下方面起着至关重要的作用:
- 应用性能
- Bill,您将从云提供商(AWS,Azure等)获得收益
这就带来了一个问题,我的应用程序正确的堆大小是多少? 我应该为应用程序分配大堆大小还是小堆大小? 答案是:“取决于”。 在本文中 ,我们分享了关于需要使用大堆还是小堆大小的想法。
您还可以考虑阅读本文: 将-Xms和-Xmx设置为相同值的优点 。
元空间是将存储JVM的元数据定义(例如类定义,方法定义)的区域。 默认情况下,可用于存储此元数据信息的内存量是无限的(即受您的容器或计算机的RAM大小的限制)。 您需要使用-XX:MaxMetaspaceSize参数来指定可用于存储元数据信息的内存量的上限。
-XX:MaxMetaspaceSize = 256m
2. GC算法
截至该日期(2020年3月),OpenJDK中有7种不同的GC算法:
- 串行GC
- 平行气相色谱
- 并发标记和扫描GC
- G1 GC
- 雪兰多GC
- Z气相色谱
- Epsilon GC
如果您未明确指定GC算法,那么JVM将选择默认算法。 在Java 8之前,并行GC是默认的GC算法。 从Java 9开始,G1 GC是默认的GC算法。
GC算法的选择对于确定应用程序的性能起着至关重要的作用。 根据我们的研究,我们使用Z GC算法观察到了出色的性能结果。 如果使用JVM 11+,则可以考虑使用Z GC算法(即-XX:+ UseZGC)。 有关Z GC算法的更多详细信息,请参见此处 。
下表总结了激活每种垃圾收集算法所需传递的JVM参数。
3.启用GC日志记录
垃圾收集日志包含有关垃圾收集事件,回收的内存,暂停时间长度等信息。...您可以通过传递以下JVM参数来启用垃圾收集日志:
从JDK 1到JDK 8:
-XX:+ PrintGCDetails -XX:+ PrintGCDateStamps -Xloggc:{文件路径}
从JDK 9及更高版本开始:
-Xlog:gc *:file = {文件路径}
例:
-XX:+ PrintGCDetails -XX:+ PrintGCDateStamps -Xloggc:/opt/workspace/myAppgc.log
-Xlog:gc *:file = / opt / workspace / myAppgc.log
通常,GC日志用于调整垃圾回收性能。 但是,GC日志包含重要的微观指标。 这些指标可用于预测应用程序的可用性和性能特征。
在本文中,我们将重点介绍一种这样的微米:“ GC吞吐量”(要了解其他可用的微米的更多信息,您可以参考本文 )。 GC吞吐量是您的应用程序在处理客户交易中花费的时间与它在处理GC活动中花费的时间之比。 假设您的应用程序的GC吞吐量为98%,则意味着该应用程序将其98%的时间用于处理客户活动,而其余2%则用于GC活动。
现在,让我们看一个健康的JVM的堆使用情况图:
您会看到一个完美的锯齿图案。 您会注意到,当运行Full GC(红色三角形)时,内存利用率会一直下降到最低。
现在,让我们看一下有问题的JVM的堆使用情况图:
您可以注意到,在图形的右端,即使GC重复运行,内存利用率也没有下降。 这是该应用程序正在遭受某种内存问题的经典征兆。
如果您仔细观察一下该图,您会发现重复的完整GC大约在上午8点开始发生。 但是,该应用程序仅在上午8:45左右开始获取OutOfMemoryError。 到上午8点,该应用程序的GC吞吐量约为99%。 但是,在早上8点之后,GC吞吐量开始下降到60%。 因为当重复的GC运行时,该应用程序将不会处理任何客户交易,而只会进行GC活动。 作为一项主动措施,如果您发现GC吞吐量开始下降,则可以从负载平衡器池中取出JVM。 这样,运行状况不佳的JVM将不会处理任何新的流量。 它将最大程度地减少客户影响。
您可以使用GCeasy REST API实时监视与GC相关的微米。
4. -XX:+ HeapDumpOnOutOfMemoryError,-XX:HeapDumpPath
OutOfMemoryError是一个严重的问题,它将影响您的应用程序的可用性/性能SLA。 为了诊断OutOfMemoryError或任何与内存相关的问题,必须在应用程序开始遇到OutOfMemoryError的那一刻或一瞬间捕获堆转储。
由于我们不知道何时会抛出OutOfMemoryError,因此很难在抛出异常的那一刻左右手动捕获堆转储。 但是,可以通过传递以下JVM参数来自动化捕获堆转储:
-XX:+ HeapDumpOnOutOfMemoryError和-XX:HeapDumpPath = {HEAP-DUMP-FILE-PATH}
在“ -XX:HeapDumpPath”中,您需要指定堆转储所在的文件路径。 当您传递这两个JVM参数时,将在抛出OutOfMemoryError时自动捕获堆转储并将其写入定义的文件路径。
例:
-XX:+ HeapDumpOnOutOfMemoryError -XX:HeapDumpPath = / crashes / my-heap-dump.hprof
捕获堆转储后,您可以使用HeapHero和Eclipse MAT之类的工具来分析堆转储。
可以在本文中找到有关OutOfMemoryError JVM参数的更多详细信息。
5. -Xss
每个应用程序将具有数十,数百,数千个线程。 每个线程都有自己的堆栈。 在每个线程的堆栈中,存储以下信息:
- 当前执行的方法/功能
- 原始数据类型
- 变数
- 对象指针
- 返回值。
他们每个人都消耗内存。 如果它们的使用量超出某个限制,则会引发StackOverflowError。 有关StackOverflowError及其解决方案的更多详细信息,请参见本文 。 但是,可以通过传递-Xss参数来增加线程的堆栈大小限制。
例:
-Xss256k
如果将此-Xss值设置为一个很大的数字,则内存将被阻塞并浪费。 假设您将-Xss值指定为2mb,而它只需要256kb,那么您将浪费大量的内存,而不仅仅是1792kb(即2mb – 256kb)。 你想知道为什么吗?
假设您的应用程序有500个线程,然后-Xss值为2mb,则您的线程将消耗1000mb的内存(即500个线程x 2mb /线程)。 另一方面,如果您已将-Xssonly分配为256kb,则您的线程将仅消耗125mb的内存(即500个线程x 256kb /线程)。 每个JVM将节省875mb(即1000mb – 125mb)内存。 是的,它将产生巨大的变化。
注意:线程是在堆(即-Xmx)之外创建的,因此这1000mb将是您已经分配的-Xmx值的补充。 要了解为什么在堆外部创建线程,您可以观看此简短视频片段 。
我们的建议是从一个低值(例如256kb)开始。 使用此设置运行彻底的回归,性能和AB测试。 仅当您遇到StackOverflowError时才增加该值,否则请考虑坚持较低的值。
6. -Dsun.net.client.defaultConnectTimeout和-Dsun.net.client.defaultReadTimeout
现代应用程序使用多种协议(即SOAP,REST,HTTP,HTTPS,JDBC,RMI…)与远程应用程序连接。 有时远程应用程序可能需要很长时间才能做出响应。 有时它可能根本不响应。
如果没有正确的超时设置,并且远程应用程序的响应速度不够快,则您的应用程序线程/资源将被卡住。 远程应用程序无响应可能会影响应用程序的可用性。 它可以使您的应用程序停止磨削。 为了保护应用程序的高可用性,应配置适当的超时设置。
您可以在JVM级别传递这两个强大的超时网络属性,这些属性可以全局适用于所有使用java.net.URLConnection的协议处理程序:
- sun.net.client.defaultConnectTimeout指定建立到主机的连接的超时(以毫秒为单位)。 例如,对于HTTP连接,这是建立与HTTP服务器的连接时的超时。
- 当建立与资源的连接时, sun.net.client.defaultReadTimeout指定从输入流读取时的超时(以毫秒为单位)。
例如,如果您要将这些属性设置为2秒:
-Dsun.net.client.defaultConnectTimeout = 2000
-Dsun.net.client.defaultReadTimeout = 2000
注意,默认情况下,这两个属性的值为-1,这表示未设置超时。 有关这些属性的更多详细信息,请参见本文 。
还请参见: 2020年Java状况:Java 8仍在领先
7. -Duser.timeZone
您的应用程序可能在某个时间/日期有敏感的业务需求。 例如,如果您正在构建交易应用程序,则无法在上午9:30之前进行交易。 为了实现那些与时间/日期相关的业务需求,您可以使用java.util.Date,java.util.Calendar对象。 默认情况下,这些对象从底层操作系统中获取时区信息。 这将成为一个问题; 如果您的应用程序在分布式环境中运行。 查看以下方案:
一个。 如果您的应用程序跨多个数据中心(例如,旧金山,芝加哥,新加坡)运行,则每个数据中心中的JVM最终将具有不同的时区。 因此,每个数据中心中的JVM将表现出不同的行为。 这将导致结果不一致。
b。 如果要在云环境中部署应用程序,则可能会在您不知情的情况下将应用程序移动到其他数据中心。 同样在这种情况下,您的应用程序最终将产生不同的结果。
C。 您自己的运营团队还可以更改时区,而无需掌握开发团队的知识。 它还会歪曲结果。
为避免这些麻烦,强烈建议使用-Duser.timezone系统属性在JVM上设置时区。 例如,如果您想为应用程序设置EDT时区,则将执行以下操作:
-Duser.timezone =美国/东部
结论
在本文中,我们试图总结一些重要的JVM参数及其积极影响。 我们希望您会发现它对您有所帮助。
程序中 设置jvm 参数