java.lang.OutOfMemoryError:压缩的类空间

时间:2015-05-28 15:51:23

标签: java garbage-collection jvm out-of-memory java-8

我们正在使用java-8-oracle。

我们六个月前搬到了java8。

在过去的几天里,我们不时收到OOME,但我们无法识别或重现问题。

当我们执行对服务器(tomcat)的调用时,我们在stacktrace上得到了这个错误:

java.lang.OutOfMemoryError: Compressed class space

重新启动服务器可以解决问题。对其他服务器的相同调用也起作用,对另一个类型的另一个调用也是如此。

查看gc.log时,我们看到:

2015-05-27T16:05:42.991+0000: 98774.440: [Full GC (Last ditch collection) 98774.440: [CMS: 575745K->575330K(3495936K), 0.8687777 secs] 575745K->575330K(4107008K), [Metaspace: 97940K->97940K(1396736K)], 0.8696093 secs] [Times: user=0.95 sys=0.00, real=0.88 secs]
2015-05-27T16:05:55.486+0000: 98786.935: [Full GC (Metadata GC Threshold) 98786.935: [CMS: 573414K->578735K(3495936K), 0.9372859 secs] 925046K->578735K(4107008K), [Metaspace: 99428K->99428K(1396736K)], 0.9386626 secs] [Times: user=1.01 sys=0.00, real=0.94 secs]

jstat -gc返回:

 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
87296.0 87296.0  0.0   3151.4 523776.0 148284.4 3495936.0   574868.5  1395640.0 98066.3 1048576.0 11339.1  12165  636.851  223   116.957  

753.808

我在jstat日志或gc日志中都没有看到任何内存问题。

尝试运行jmap -clstats挂起:

Attaching to process ID 5110, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.25-b02
finding class loader instances ..

3 个答案:

答案 0 :(得分:11)

我们遇到了类似的问题。不幸的是,heapdumps不会帮助你,因为类不在堆中而是在本机内存中。在JVM设置中启用它们以对加载的类进行故障排除:

wget "YOUR_IDP_DESCRIPTOR_URL";

在我们的案例中,问题是JAXBContext.newInstance不是单身。

祝你好运, 阿尔伯特

答案 1 :(得分:6)

使用压缩的oops和压缩类指针,由于必要的指针修改,类的可用空间受到限制。在您的情况下为1GB。

这是很多类,所以这可能表明你的应用程序中的某些东西正在创建很多类而从不发布它们。应用程序重新加载可能吗?

如果您确定您的应用程序只需要那么多内存,您可以尝试通过-XX:CompressedClassSpaceSize=...突破限制或通过-XX:-UseCompressedClassPointers禁用压缩类指针。

注意默认情况下压缩类空间+压缩堆(+一些开销)不能超过32GB。虽然,AIUI,改变对象对齐可以进一步突破这个限制。

否则,您应该使用heapdump并分析保存在已加载类中的内容。

答案 2 :(得分:0)

您可以使用以下两个选项之一来解决此问题。

1。您可以使用-XX:CompressedClassSpaceSize = n VM标志来增加类空间的限制。上限为4GB。

(OR)

2。您可以使用-XX:-UseCompressedClassPointers VM标志完全禁用压缩的类指针功能。

注意: 请注意,禁用此功能会增加应用程序的堆空间使用率。

摘自《 Java性能优化:压缩的OOPS》一书

每个对象都有一个指向VM元数据类的指针(_klass)。这个指针 也可以在64位JVM上压缩。在JDK 8中,永久生成是 删除并替换为元空间内存。这个元空间是 动态增长的内存,并且如果VM可以达到本机内存限制 标志未配置为控制此元空间内存。先前的Java 在图8中,使用了永久内存,其大小由VM标志固定。它 在运行时无法更改其大小。与堆内存相比 空间,此永久内存空间(非堆)非常小,并且会 通常在4GB的范围内以存储类元数据和其他 元数据。因此,虚拟内存地址范围可以是0到4 GB(如果 从零开始的虚拟内存由操作系统分配给 永久记忆。所以压缩klass对象的内存地址 在此永久内存位置仅使用32位不是问题。

但是在Java 8中,永久内存被替换为元空间,以便 支持一些性能改进。该内存位置可以增长 直到达到本机内存限制,如果-XX:MaxMetaspaceSize VM为 不能控制这种增长。如果可以增长到本机内存 限制,然后将klass对象也存储在更高的位置 内存边界。压缩该内存将是一个问题 使用32位偏移量定位。所以为了处理这个问题 为单独的klass实例分配单独的内存空间,Java 在Java中引入了一个新的存储空间,称为“压缩类空间” 8版本。 Java 8中包含压缩的类空间内存 引入了“ Java.lang.OutOfMemoryError:压缩的类空间”。这个 压缩类空间内存时触发OutOfMemoryError 达到其最大默认容量1 GB,并且没有更多 存储其他Klass实例的空间

为了了解有关此OutOfMemoryError和提交的klass的更多详细信息,可以阅读以下书籍。

https://javaperformanceoptimization.com/

  1. Java性能优化:压缩的OOPS
  2. Java性能优化:如何避免10个OutOfMemoryErrors