李绍志 發表於 2025-12-26 11:01:00

jvm~jvm配置与系统配置的关系

<h1 id="一-生产环境问题描述">一 生产环境问题描述</h1>
<p>keycloak是运行在jboss上面,并且部署到了容器里,在k8s上面进行编排,现在遇到gc在某个时刻垃圾回收速度变慢,CPU接近100%,容器导致存活探针失败,容器最后重启</p>
<p><img src="https://img2024.cnblogs.com/blog/118538/202512/118538-20251226105937326-500894156.png" alt="图片" loading="lazy"></p>
<h1 id="二-问题分析与解决方案">二 问题分析与解决方案</h1>
<p>这是一个典型的 JVM GC 问题导致的容器重启场景。让我分析一下你的配置和可能的原因:</p>
<h2 id="21-问题分析">2.1 问题分析</h2>
<p>根据你的 <code>devops.yaml</code> 配置:</p>
<pre><code class="language-88:88:service-spi/deploy/test/devops.yaml">            value: "-Xms1024M -Xmx1024M -XX:ConcGCThreads=13 -XX:ParallelGCThreads=13 -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -Djboss.bind.address.management=0.0.0.0"
</code></pre>
<pre><code class="language-54:60:service-spi/deploy/test/devops.yaml">          resources:
            limits:
            memory: 10Gi   # 限制的内存量
            cpu: 2       # 限制的 CPU 核心数
            requests:
            cpu: 100m
            memory: 500m
</code></pre>
<pre><code class="language-61:70:service-spi/deploy/test/devops.yaml">          livenessProbe: #存活检测
            failureThreshold: 2 #探测失败的重试次数,重试一定次数后将认为失败,在 readiness 探针中,Pod会被标记为未就绪,默认为 3,最小值为 1
            initialDelaySeconds: 120 #容器启动后要等待多少秒后就探针开始工作,单位"秒",默认是 0 秒,最小值是 0
            periodSeconds: 10 #执行探测的时间间隔(单位是秒),默认为 10s,单位"秒",最小值是 1
            successThreshold: 1 #探针检测失败后认为成功的最小连接成功次数,默认为 1
            timeoutSeconds: 5 #探针执行检测请求后,等待响应的超时时间,默认为 1s,单位"秒",最小值是 1
            httpGet:
            path: /auth
            scheme: HTTP
            port: 8080
</code></pre>
<hr>
<h2 id="-22-发现的关键问题">🔴 2.2 发现的关键问题</h2>
<h3 id="1-gc-线程数配置过高">1. <strong>GC 线程数配置过高</strong></h3>
<pre><code>-XX:ConcGCThreads=13 -XX:ParallelGCThreads=13
</code></pre>
<p><strong>问题</strong>:你配置了 13 个并发 GC 线程和 13 个并行 GC 线程,但 CPU 限制只有 <strong>2 核</strong>!</p>
<ul>
<li><strong>ParallelGCThreads=13</strong>:并行 GC 阶段使用 13 个线程</li>
<li><strong>ConcGCThreads=13</strong>:并发标记阶段使用 13 个线程</li>
</ul>
<p>当 13 个线程竞争 2 个 CPU 核心时,会导致:</p>
<ul>
<li>严重的 CPU 上下文切换</li>
<li>GC 线程互相等待 CPU 时间片</li>
<li>GC 暂停时间变长</li>
<li>CPU 使用率飙升到 100%</li>
</ul>
<p><strong>推荐配置</strong>:</p>
<pre><code>-XX:ParallelGCThreads=2 -XX:ConcGCThreads=1
</code></pre>
<blockquote>
<p><strong>公式参考</strong>:</p>
<ul>
<li><code>ParallelGCThreads</code> = CPU 核心数(或稍少)</li>
<li><code>ConcGCThreads</code> = max(1, ParallelGCThreads / 4)</li>
</ul>
</blockquote>
<hr>
<h3 id="2-堆内存设置过小">2. <strong>堆内存设置过小</strong></h3>
<pre><code>-Xms1024M -Xmx1024M
</code></pre>
<p><strong>问题</strong>:Keycloak 是一个比较重的应用,1GB 堆内存可能不够用,尤其是:</p>
<ul>
<li>用户会话多时</li>
<li>存在大量认证请求时</li>
<li>Infinispan 缓存占用内存</li>
</ul>
<p>这会导致:</p>
<ul>
<li>频繁触发 GC</li>
<li>Full GC 增多</li>
<li>GC 时间变长</li>
</ul>
<p><strong>推荐</strong>:容器内存限制是 10Gi,可以适当增加堆内存:</p>
<pre><code>-Xms2048M -Xmx4096M
</code></pre>
<hr>
<h3 id="3-存活探针配置过于严格">3. <strong>存活探针配置过于严格</strong></h3>
<pre><code class="language-yaml">livenessProbe:
failureThreshold: 2      # 只允许失败 2 次
periodSeconds: 10      # 每 10 秒探测一次
timeoutSeconds: 5      # 超时 5 秒
</code></pre>
<p><strong>问题</strong>:当发生 Full GC 或长时间 GC 暂停时:</p>
<ul>
<li>GC STW(Stop The World)可能超过 5 秒</li>
<li>只需要 2 次失败(20 秒内)就会重启容器</li>
<li>这个配置对 Java 应用过于苛刻</li>
</ul>
<p><strong>推荐调整</strong>:</p>
<pre><code class="language-yaml">livenessProbe:
failureThreshold: 3      # 允许 3 次失败
periodSeconds: 15      # 每 15 秒探测
timeoutSeconds: 10       # 超时 10 秒
</code></pre>
<hr>
<h3 id="4-缺少关键的-g1gc-调优参数">4. <strong>缺少关键的 G1GC 调优参数</strong></h3>
<p>当前配置缺少:</p>
<ul>
<li>最大 GC 暂停时间目标</li>
<li>G1 Region 大小</li>
<li>堆内存使用率阈值</li>
</ul>
<hr>
<h1 id="三-优化java_opts配置">三 优化JAVA_OPTS配置</h1>
<p><strong>问题</strong>:<code>-XX:+PrintGCDetails</code> 在 Java 9+ 已被弃用,应该使用统一的 <code>-Xlog</code> 语法。<br>
优化建议的完整 JAVA_OPTS</p>
<p><strong>非堆内存限制</strong></p>
<pre><code class="language-yaml">-XX:MaxMetaspaceSize=512M
-XX:MaxDirectMemorySize=512M
-XX:ReservedCodeCacheSize=256M
</code></pre>
<p><strong>并行与并发配置过于保守,可根据CPU核数来配置,如<code>4核的CPU配置如下</code></strong></p>
<ul>
<li>ParallelGCThreads 并行GC线程数(约为CPU核数的5/8)</li>
<li>ConcGCThreads 并发GC线程数(约为ParallelGCThreads的1/4)</li>
</ul>
<pre><code class="language-yaml">-XX:+UseG1GC
-XX:ParallelGCThreads=4
-XX:ConcGCThreads=1
-XX:MaxGCPauseMillis=200
</code></pre>
<h3 id="完整的java_opts配置">完整的JAVA_OPTS配置</h3>
<pre><code class="language-yaml">- name: JAVA_OPTS
value: &gt;-
    -Xms10240M
    -Xmx10240M
    -XX:+UseG1GC
    -XX:ParallelGCThreads=5
    -XX:ConcGCThreads=2
    -XX:MaxGCPauseMillis=200
    -XX:G1HeapRegionSize=16M
    -XX:InitiatingHeapOccupancyPercent=45
    -XX:MaxMetaspaceSize=512M
    -XX:MaxDirectMemorySize=512M
    -XX:ReservedCodeCacheSize=256M
    -XX:+HeapDumpOnOutOfMemoryError
    -XX:HeapDumpPath=/tmp/heapdump.hprof
    -Xlog:gc*:file=/tmp/gc.log:time,uptime,level,tags
    -Djboss.bind.address.management=0.0.0.0
</code></pre>


</div>
<div id="MySignature" role="contentinfo">
    <p></p>
<div class="navgood">
<p>作者:仓储大叔,张占岭,<br>
荣誉:微软MVP<br>QQ:853066980</p>

<p><strong>支付宝扫一扫,为大叔打赏!</strong>
<br><img src="https://images.cnblogs.com/cnblogs_com/lori/237884/o_IMG_7144.JPG"></p>
</div><br><br>
来源:https://www.cnblogs.com/lori/p/19403589
頁: [1]
查看完整版本: jvm~jvm配置与系统配置的关系