性能文章>Java OOM 基础篇:常见的OutOfMemoryError 场景三: PermGen space 永久空间问题详解>

Java OOM 基础篇:常见的OutOfMemoryError 场景三: PermGen space 永久空间问题详解转载

6月前
3242121

Java OOM优化专题大纲目录

 

导语

此文来自于plumbr官网,plumbr作为一个常用的JVM 监测工具,官网有完整的oom和gc文章,准备慢慢全部翻译过来:

第一篇:java.lang.OutOfMemoryError:Java heap space

第二篇:Java.lang.OutOfMemoryError: GC overhead limit exceeded

第三篇:java.lang.OutOfMemoryError: 永久空间

本文解读了永久空间问题,是什么造成了永久空间问题,3个解决永久空间问题的方法,大家可以查缺补漏,希望本文能对你有所帮助。

正文:

Java 应用程序只允许使用有限的内存量。您的特定应用程序可以使用的确切内存量是在应用程序启动期间指定的。为了让事情变得更复杂,Java 内存被分成不同的区域,如下图所示:

java.lang.outofmemoryerror: Permgen 空间

所有这些区域的大小,包括 permgen 区域,都是在 JVM 启动期间设置的。如果您不自己设置大小,将使用特定于平台的默认值。

java.lang.OutOfMemoryError:PermGen space的消息表明永久代的内存区域被耗尽

1,什么原因造成的?

要了解java.lang.OutOfMemoryError: PermGen space 的原因,我们需要了解此特定内存区域的用途。

出于实际目的,永久代主要由加载并存储到 PermGen 中的类声明组成。这包括类的名称和字段、带有方法字节码的方法、常量池信息、与类关联的对象数组和类型数组以及即时编译器优化。

从上面的定义中,您可以推断出永久代的大小要求取决于加载的类的数量以及此类声明的大小。因此,我们可以说java.lang.OutOfMemoryError: PermGen space 的主要原因是加载到永久代的类太多或类太大

2,举个例子

如上所述,永久代空间的使用与加载到 JVM 中的类数量密切相关。下面的代码是最直接

导入 javassist.ClassPool; 

public class MicroGenerator { 
  public static void main(String[] args) 抛出异常 { 
    for (int i = 0; i < 100_000_000; i++) { 
      generate("eu.plumbr.demo.Generated" + i); 
    } 
  } 

  public static Class generate(String name) throws Exception { 
    ClassPool pool = ClassPool.getDefault(); 
    返回 pool.makeClass(name).toClass(); 
  } 
}

在这个例子中,源代码遍历一个循环并在运行时生成类。javassist库负责处理类生成的复杂性。

启动上面的代码将继续生成新类并将它们的定义加载到永久空间中,直到空间被完全利用并抛出java.lang.OutOfMemoryError: Permgen 空间

重新部署时间示例

对于更复杂和更现实的示例,让我们带您了解在应用程序重新部署期间发生的java.lang.OutOfMemoryError: Permgen space错误。当您重新部署应用程序时,您会期望垃圾回收将摆脱引用所有先前加载的类的先前类加载器,并将其替换为加载类的新版本的类加载器。

不幸的是,许多 3rd 方库和对资源(例如线程、JDBC 驱动程序或文件系统句柄)的处理不当使得无法卸载以前使用的类加载器。这反过来意味着在每次重新部署期间,您的类的所有先前版本仍将驻留在 PermGen 中,在每次重新部署期间生成数十兆字节的垃圾

让我们想象一个使用 JDBC 驱动程序连接到关系数据库的示例应用程序。当应用程序启动时,初始化代码加载 JDBC 驱动程序以连接到数据库。对应于规范,JDBC 驱动程序使用java.sql.DriverManager注册自己。此注册包括在DriverManager的静态字段中存储对驱动程序实例的引用。

现在,当应用程序从应用程序服务器中卸载时,java.sql.DriverManager仍将保留该引用。我们最终获得了对驱动程序类的实时引用,该类又包含对用于加载应用程序的java.lang.Classloader实例的引用。这反过来意味着垃圾收集算法无法回收空间。

并且java.lang.ClassLoader 的那个实例仍然引用应用程序的所有类,通常在 PermGen 中占用数十兆字节。这意味着只需重新部署几次即可填充通常大小的 PermGen 并在日志中获取java.lang.OutOfMemoryError: PermGen space错误消息。

3,解决办法是什么?

1.解决初始化时OutOfMemoryError

当应用程序启动时触发由于 PermGen 耗尽导致的 OutOfMemoryError 时,解决方案很简单。应用程序只需要更多空间将所有类加载到 PermGen 区域,所以我们只需要增加它的大小。为此,请更改您的应用程序启动配置并添加(或增加(如果存在))类似于以下示例的-XX:MaxPermSize参数:

java -XX:MaxPermSize=512m com.yourcompany.YourClass

上述配置将告诉 JVM,允许 PermGen 增长到 512MB,然后才能开始以 OutOfMemoryError 的形式抱怨。

2.解决重新部署时OutOfMemoryError

当您重新部署应用程序后立即发生 OutOfMemoryError 时,您的应用程序会遭受类加载器泄漏。在这种情况下,解决问题的最简单,最直接的方式就是用工具排查,找到有问题的代码,并解决它以分钟为单位。

对于那些不能使用 Plumbr 或决定不使用的人,也可以使用替代方法。为此,您应该继续进行堆转储分析 - 在重新部署后使用类似于以下命令的命令进行堆转储:

jmap -dump:format=b,file=dump.hprof <process-id>

然后使用您最喜欢的堆转储分析器打开转储(Eclipse MAT 是一个很好的工具)。在分析器中,您可以查找重复的类,尤其是那些加载应用程序类的类。从那里,您需要进入所有类加载器以找到当前活动的类加载器。

对于不活动的类加载器,您需要通过从不活动的类加载器获取到GC 根的最短路径来确定阻止它们被垃圾收集的引用。有了这些信息,您就会找到根本原因。如果根本原因在 3rd 方库中,您可以继续访问 Google/StackOverflow 以查看这是否是获取补丁/解决方法的已知问题。如果这是您自己的代码,则需要删除违规引用。

3.解决运行时OutOfMemoryError

对于那些再次无法使用 Plumbr 的人,也可以使用另一种方法。在这种情况下,第一步是检查是否允许 GC 从 PermGen 卸载类。标准的 JVM 在这方面相当保守——类天生就是为了永生。所以一旦加载,即使没有代码再使用它们,类也会留在内存中。当应用程序动态创建大量类并且长时间不需要生成的类时,这可能会成为一个问题。在这种情况下,允许 JVM 卸载类定义会很有帮助。这可以通过向启动脚本添加一个配置参数来实现:

-XX:+CMSClassUnloadingEnabled

默认情况下,它设置为 false,因此要启用它,您需要在 Java 选项中显式设置以下选项。如果您启用CMSClassUnloadingEnabledGC 也会清除PermGen 并删除不再使用的类。请记住,此选项仅在使用以下选项启用UseConcMarkSweepGC时才有效。因此,当运行ParallelGC或,上帝保佑,Serial GC 时,请确保您已通过指定将 GC 设置为CMS

-XX:+UseConcMarkSweepGC

在确保可以卸载类并且问题仍然存在后,您应该继续进行堆转储分析 - 使用类似于以下的命令进行堆转储:

jmap -dump:file=dump.hprof,format=b <process-id>

然后使用您最喜欢的堆转储分析器(例如 Eclipse MAT)打开转储,并根据加载的类数量继续查找最昂贵的类加载器。从这样的类加载器中,您可以继续提取加载的类并按实例对此类类进行排序,以获得可疑的顶部列表。

对于每个嫌疑人,您需要手动将根本原因追溯到生成此类类的应用程序代码。

请先登录,查看1条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

Java OOM 高级篇:线上Docker 上Springboot程序OOM问题的排查分享
背景运维人员反馈一个容器化的java程序每跑一段时间就会出现OOM问题,重启后,间隔大概两天后复现。 问题调查 一、查日志由于是容器化部署的程序,登上主机后使用docker logs Containe
Java OOM 高级篇:体验了一把线上CPU100%及应用OOM的排查和解决过程
问题现象 【告警通知-应用异常告警】简单看下告警的信息:拒绝连接,反正就是服务有问题了,请不要太在意马赛克。 环境说明Spring Cloud F版。项目中默认使用 spring-cloud-sleu
Java OOM 实战篇:应用故障之Java heap space 堆溢出实战
以下是用于测试OOM的测试代码:```javapublic class HeapMemUseTest { public static void main(String[] args) {
Java OOM 原理篇 : 什么是 Java OOM
明明只是小白、明明只想找份工作、明明没有机会接触到OOM与调优,却被现实逼着要去搞懂JVM、OOM、调优。看完这篇文章,大家能获得:什么是OOM、为什么会发生OOM、哪些区域会发生OOM、JVM进程挂了,会有哪些可能性、生产环境的JVM无响应了,如何快速定位问题、子牙老师给你的一些成熟的调优建议