当前位置: 首页 > 知识库问答 >
问题:

kubernetes吊舱内存-java gc日志

咸浩初
2023-03-14

在kubernetes仪表板上,有一个pod,其中内存使用情况(字节)显示为904.38Mi

这个pod保存了使用Xms512m-Xmx1024m运行的java应用程序,该应用程序位于kubernetes部署文件中-

我已启用gc日志,并在pod日志中看到这些日志:

[2020-04-29T15:41:32.051+0000] GC(1533) Phase 1: Mark live objects
[2020-04-29T15:41:32.133+0000] GC(1533) Phase 1: Mark live objects 81.782ms
[2020-04-29T15:41:32.133+0000] GC(1533) Phase 2: Compute new object addresses
[2020-04-29T15:41:32.145+0000] GC(1533) Phase 2: Compute new object addresses 11.235ms
[2020-04-29T15:41:32.145+0000] GC(1533) Phase 3: Adjust pointers
[2020-04-29T15:41:32.199+0000] GC(1533) Phase 3: Adjust pointers 54.559ms
[2020-04-29T15:41:32.199+0000] GC(1533) Phase 4: Move objects
[2020-04-29T15:41:32.222+0000] GC(1533) Phase 4: Move objects 22.406ms
[2020-04-29T15:41:32.222+0000] GC(1533) Pause Full (Allocation Failure) 510M->127M(680M) 171.359ms
[2020-04-29T15:41:32.222+0000] GC(1532) DefNew: 195639K->0K(195840K)
[2020-04-29T15:41:32.222+0000] GC(1532) Tenured: 422769K->130230K(500700K)
[2020-04-29T15:41:32.222+0000] GC(1532) Metaspace: 88938K->88938K(1130496K)
[2020-04-29T15:41:32.228+0000] GC(1532) Pause Young (Allocation Failure) 603M->127M(614M) 259.018ms
[2020-04-29T15:41:32.228+0000] GC(1532) User=0.22s Sys=0.05s Real=0.26s

kubernetes是如何到达904.38Mi用法的?如果我理解正确,目前的用法只有:

DefNew (young) -      0k
Tenured        - 130230K
Methtml" target="_blank">aspace      -  88938K
Sum            - 216168K

运行ps显示除了这个java应用程序之外,pod上没有其他进程在运行<任何人都能对此有所了解?

(已编辑)当pod第一次启动并运行几分钟时,内存使用量仅显示为500mb左右,然后让请求进入,它将突发到900mb-1gb,然后在所有处理完毕后,k8s仪表板上的内存使用量不会下降到900mb以下,即使根据GC日志,堆是GC'ed ok。

共有2个答案

归泽宇
2023-03-14

GC处理进程使用的内存子集。JVM内存中有一些区域不是垃圾收集的对象。

下面是一些不包含在堆/元空间中的内存区域

  • 线程堆栈空间

上面的列表并不完整,它们只是最大的内存消耗者。

下面是JVM内存层次结构图,其中包含相关的配置选项。

总之,实际的JVM内存需求总是大于堆限制。

大小取决于应用程序的性质,可以根据经验确定。

更新

可以在JVM中启用Java本机内存跟踪,以提供有关不同功能区域内存使用情况的详细报告。

苏墨竹
2023-03-14

这里发生了很多事情。让我们一次一个。

似乎每个pod使用一个容器(尽管每个pod可能有许多容器)。requests.memorylimits.memory特定于容器,库伯内特斯计算每个pod的限制请求作为所有容器限制的总和。

想想看,你说的是一个吊舱显示904.38Mi,但你显示的是请求。内存和限制。每个容器的内存。这就是为什么我假设每个吊舱有一个容器。这是一个一般性的介绍,并没有回答您的问题,但我们会做到这一点。

然后,一个pod由docker启动,它是由kubectl启动的,读起来需要。内存和限制。内存。让这更简单一点:您在<代码>限制中设置了什么。内存将作为docker-m传递。因此,在您的情况下,docker进程使用的总内存为1.5GC。请记住,这是整个进程的限制,而不仅仅是堆。java进程比堆要多得多,堆是用Xms512m-Xmx1024m指定的。因此,要回答您的问题:

kubernetes是如何达到904.38Mi的使用率的?

这是整个进程当前所做的,而不仅仅是堆。从您发布的很短的日志文件来看,您的应用程序很好。

编辑

实际上,我的环境中没有kubernetes仪表板来专门测试这个,所以必须安装它才能真正了解发生了什么。我对大多数事情都有一个提示,但为了确保,我做了一些测试。

第一件事:仪表板上的数字是什么意思?花了一段时间才找到/理解,但这是该过程的实际驻留内存,这实际上是一件非常好的事情。

任何理智的OS都知道,当有人向它请求内存时,它很少需要/利用它,因此,它以懒惰的方式将内存提供给它。这在k8s中很容易证明。假设我有一个jdk-13JVM并以:

kubectl run jdk-13 
    --image=jdk-13 
    --image-pull-policy=Never 
    --limits "memory=100Mi" 
    --requests "memory=10Mi" 
    --command -- /bin/sh -c "while true; do sleep 5; done".

请注意请求。内存=10Mi和限制。内存=100Mi。从一开始就阅读答案,您已经知道特定pod将从docker-m 100m开始 因为限制。内存=100Mi。这很容易证明,只需将sh放入pod即可:

 kubectl exec -it jdk-13-b8d656977-rpzrg -- /bin/sh

并找出cgroup所说的:

 # cat /sys/fs/cgroup/memory/memory.limit_in_bytes
 104857600 // 100MB

完美的所以pod的内存限制是100 MB,但是当前的内存利用率是多少,即占用的常驻内存是多少?

kubectl top pod
   NAME                          CPU(cores)   MEMORY(bytes)
   jdk-13-b8d656977-rpzrg           1m           4Mi

好的,所以当前内存利用率只有4MB。如果您做到以下几点,您可以“确保”这确实是准确的:

kubectl exec -it jdk-13-b8d656977-rpzrg -- /bin/sh

在pod问题中:

top -o %MEM

请注意,RES内存与通过仪表板或kubectl top pod报告的内存相同。

现在让我们做一个测试。假设我在那个pod中有这个非常简单的代码:

// run this with: java "-Xlog:gc*=debug" -Xmx100m -Xms20m  HeapTest
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

public class HeapTest {

    public static void main(String[] args) throws Exception {

        // allocate 1 MB every 3 seconds
        for (int i = 0; i < 40; ++i) {
            byte[] b = new byte[1024 * 1024 * 1];
            b[i] = 1;
            System.out.println(Arrays.hashCode(b));
            LockSupport.parkNanos(TimeUnit.of(ChronoUnit.SECONDS).toNanos(3));
        }
    }
}

    

我每3秒钟分配1MB,大约2分钟。当我在仪表板中查看此过程时,我确实看到在某个时间点,内存会增长。程序结束后,dashboard报告内存下降。好的这意味着内存被退回,RSS内存下降。这是仪表板中的外观:

现在让我们稍微更改一下这段代码。让我们在其中添加一些GC,让我们永远不会完成这个过程(你知道,就像典型的Spring启动应用程序一样):

import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

public class HeapTest {

    public static void main(String[] args) throws Exception {

        // allocate 1 MB every 3 seconds
        for (int i = 0; i < 40; ++i) {
            byte[] b = new byte[1024 * 1024 * 1];
            b[i] = 1;
            System.out.println(Arrays.hashCode(b));
            LockSupport.parkNanos(TimeUnit.of(ChronoUnit.SECONDS).toNanos(3));
        }
        for (int i = 0; i < 10; i++) {
            Thread.sleep(500);
            System.gc();
        }
        
        while (true) {
            try {
                Thread.sleep(TimeUnit.of(ChronoUnit.SECONDS).toMillis(5));
                Thread.onSpinWait();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

    }
}

我运行这个:

java "-Xlog:heap*=debug" 
     "-Xlog:gc*=debug" 
     "-Xlog:ergo*=debug" 
     -Xmx100m 
     -Xms20m
     HeapTest

在检查日志(就像您的示例中一样)时,我确实看到堆被收集得很好。但当我查看仪表板时,内存并没有下降(与前面的示例不同)。

一旦G1GC占用内存,它就不会急于将其返回给操作系统。在极少数情况下,它可以做到这一点,这里有一个例子,或者您可以指示它这样做。

这两种方法都相当痛苦,相反,GC算法更聪明(通常更好)。我个人的爱去了Shenandoah,让我们看看它有什么作用。如果我稍微修改一下代码(以便更好地证明我的观点):

import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

public class HeapTest {

    public static void main(String[] args) throws Exception {

        // allocate 1/4 MB every 100 ms
        for (int i = 0; i < 6000; ++i) {
            byte[] b = new byte[1024 * 256];
            b[i] = 1;
            System.out.println(Arrays.hashCode(b));
            LockSupport.parkNanos(TimeUnit.of(ChronoUnit.MILLIS).toNanos(100));
        }

        while (true) {
            try {
                Thread.sleep(TimeUnit.of(ChronoUnit.SECONDS).toMillis(5));
                Thread.onSpinWait();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

    }
}

并运行它:

 java "-Xlog:gc*=debug" 
      "-Xlog:ergo*=debug" 
      "-Xlog:heap*=debug" 
       -XX:+UnlockExperimentalVMOptions 
       -XX:+UseShenandoahGC 
       -XX:+ShenandoahUncommit 
       -XX:ShenandoahGCHeuristics=compact  
       -Xmx1g 
       -Xms1m  
       HeapTest

下面是您将看到的内容:

事实上,你应该关心这个:

在资源按使用付费的容器环境中,这种行为尤其不利。即使在VM由于不活动而只使用其分配内存资源的一小部分的阶段,G1也会保留所有Java堆。这导致客户始终为所有资源付费,而云提供商无法充分利用其硬件。

P、 我还要补充一个事实,其他的豆荚也在受苦,因为一个豆荚决定在某个特定的峰值上尽可能多地占用内存,而且永远不会归还。

 类似资料:
  • 我想看看kubernetes吊舱使用了多少内存和CPU。我为此运行了以下命令: 我收到以下错误: > 如果我们进入pod并运行linux命令,我们会得到相同的输出吗?

  • 我有Kubernetes设置和运行一个grpc服务在一个吊舱。我成功地击中了服务上的一个endpoint,其中有一个print()语句,但在日志文件中没有看到日志。我以前在Kubernetes中运行(cron)作业时见过这种情况,日志只在作业完成后才出现(而不是在作业运行时)。有没有一种方法可以让kubernetes立即写入日志文件?我可以放置任何设置(集群级或仅用于吊舱)?提前感谢您的任何帮助!

  • 我对Kubernetes是新来的。 我发现了2个pod优先级选项-优先级类别和服务质量。它们之间有什么不同? (https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/https://kubernetes.io/docs/tasks/configure-pod-container/quality-servi

  • 我的要求是在自定义指标上扩展POD,如队列中的挂起消息,PODS必须增加以处理作业。在kubernetes,Scale up在普罗米修斯适配器和普罗米修斯操作员中工作得很好。 我在pods中有长时间运行的进程,但HPA检查自定义度量并试图缩小规模,因为这个进程杀死了操作的中间并丢失了消息。我如何控制HPA只杀死没有进程运行的自由豆荚。 序列查询:‘{namespace=“default”,serv

  • 我们正在使用Docker 1.19运行库伯内特斯(1.18) Container是一个基于Java13的Spring启动应用程序(使用基本图像作为openjdk: 13-alpin),下面是内存设置。 豆荚: 内存-最小448M,最大2500M cpu-最小值0.1 容器: Xms:256M,Xmx:512M 当流量发送更长时间时,容器会突然重新启动;在Prometheus中,我可以看到Pod内存

  • kubernetes吊舱日志的绝对路径是什么? 当我尝试时,我可以看到吊舱日志。我想知道运行pod的日志文件路径。 kubectl原木npapp-0r9jw 在哪里可以看到绝对日志文件路径?