永久代何时被收集?

发布于 2024-12-14 02:28:58 字数 2093 浏览 0 评论 0原文

我开发的 Tomcat 应用程序使用 CMS 收集器和内存条来触发 GC。当我重新加载 web 应用程序时,有时会遇到这样的情况:旧代已满,足以触发 GC,但失效的类加载器不会被收集。

我读到类被分配到 perm gen 中,并猜测它们因此被 Old gen 集合忽略。我编写了以下测试类来测试这个理论。

package test;

import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.io.IOUtils;

/*
 JVM Options:
 -server -XX:+UseMembar -XX:+UseConcMarkSweepGC
 -XX:+UseParNewGC -XX:CMSInitiatingOccupancyFraction=80
 -XX:+UseCMSInitiatingOccupancyOnly -Xms100m -Xmx100m
 -XX:PermSize=100m -XX:NewSize=10m -XX:MaxNewSize=10m
 -verbose:gc -Xloggc:gc.log  -XX:+PrintGCTimeStamps
 -XX:+PrintGCDetails
 */
public class ClassLoaderTest extends ClassLoader
{
  @Override
  protected synchronized Class<?> loadClass(String xiName, boolean xiResolve)
      throws ClassNotFoundException
  {
    if (xiName.equals("test"))
    {
      // When asked to load "test", load Example.class
      Class<?> c = Example.class;
      String className = c.getName();
      String classAsPath = className.replace('.', '/') + ".class";
      InputStream stream = c.getClassLoader().getResourceAsStream(classAsPath);
      byte[] classData = null;
      try
      {
        classData = IOUtils.toByteArray(stream);
      }
      catch (IOException e)
      {
        e.printStackTrace();
      }
      return defineClass(className, classData, 0, classData.length);
    }
    return super.loadClass(xiName, xiResolve);
  }

  public static class Example {}
  public static ClassLoaderTest classLoaderTest;

  public static void main(String[] args) throws Exception
  {
    // Allocate CL in new gen
    classLoaderTest = new ClassLoaderTest();

    // Load a class - allocated in perm gen
    classLoaderTest.loadClass("test");

    // Discard CL
    classLoaderTest = null;

    // Pause at end
    Thread.sleep(99 * 60 * 1000);
  }

  public final byte[] mMem = new byte[85 * 1024 * 1024];
}

我运行这个类并使用 VisualVM 监视输出,发现确实发生了多个 Old 和 Young gen 集合,而没有收集失效的 Classloader,因此大字节数组保留在内存中。

VisualVM Visual GC

什么会触发 Perm Gen 被收集?

I work on a Tomcat application which uses the CMS collector along with a memory bar to trigger GC. When I reload webapps I sometimes end up in a situation where the Old gen is full enough to trigger GC but the dead Classloaders don't get collected.

I read that Classes are allocated into the perm gen and guessed that they were therefore being ignored by the Old gen collections. I wrote the following test class to test this theory.

package test;

import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.io.IOUtils;

/*
 JVM Options:
 -server -XX:+UseMembar -XX:+UseConcMarkSweepGC
 -XX:+UseParNewGC -XX:CMSInitiatingOccupancyFraction=80
 -XX:+UseCMSInitiatingOccupancyOnly -Xms100m -Xmx100m
 -XX:PermSize=100m -XX:NewSize=10m -XX:MaxNewSize=10m
 -verbose:gc -Xloggc:gc.log  -XX:+PrintGCTimeStamps
 -XX:+PrintGCDetails
 */
public class ClassLoaderTest extends ClassLoader
{
  @Override
  protected synchronized Class<?> loadClass(String xiName, boolean xiResolve)
      throws ClassNotFoundException
  {
    if (xiName.equals("test"))
    {
      // When asked to load "test", load Example.class
      Class<?> c = Example.class;
      String className = c.getName();
      String classAsPath = className.replace('.', '/') + ".class";
      InputStream stream = c.getClassLoader().getResourceAsStream(classAsPath);
      byte[] classData = null;
      try
      {
        classData = IOUtils.toByteArray(stream);
      }
      catch (IOException e)
      {
        e.printStackTrace();
      }
      return defineClass(className, classData, 0, classData.length);
    }
    return super.loadClass(xiName, xiResolve);
  }

  public static class Example {}
  public static ClassLoaderTest classLoaderTest;

  public static void main(String[] args) throws Exception
  {
    // Allocate CL in new gen
    classLoaderTest = new ClassLoaderTest();

    // Load a class - allocated in perm gen
    classLoaderTest.loadClass("test");

    // Discard CL
    classLoaderTest = null;

    // Pause at end
    Thread.sleep(99 * 60 * 1000);
  }

  public final byte[] mMem = new byte[85 * 1024 * 1024];
}

I ran this class and monitored the output using VisualVM and saw that there were indeed multiple Old and Young gen collections happening without the dead Classloader being collected and therefore the large byte array remained in memory.

VisualVM Visual GC

What would trigger the Perm Gen to be collected?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

遮云壑 2024-12-21 02:28:58

查看 JVM 选项“CMSPermGenSweepingEnabled”和“CMSClassUnloadingEnabled”。
(网上有很多关于它们的使用的讨论。)特别是,第一个假设将 PermGen 包含在垃圾收集运行中。默认情况下,PermGen 空间永远不会包含在垃圾回收中(因此会无限增长)。

Check out JVM options "CMSPermGenSweepingEnabled" and "CMSClassUnloadingEnabled".
(There are plenty of discussions on the net re their use.) In particular, the first one suppose to include the PermGen in a garbage collection run. By default, the PermGen space is never included in garbage collection (and thus grows without bounds).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文