JVM设置对象直接进入年老代

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/21aspnet/article/details/89406168

1.先说明一个事情就是在jdk1.8之后已经没有永久代被元空间取代,那么元空间在哪里

说明:元空间在本地内存,自然更不是所谓年老代等分代里。

Java PermGen去了哪里?

Java虚拟机(JVM)使用其类的内部表示,包含每个类的元数据,如类层次结构信息,方法数据和信息(如字节码,堆栈和变量大小),运行时常量池和已解析的符号引用和Vtables 。

在过去(当自定义类加载器不常见时),类大多是“静态的”并且很少被卸载或收集,因此被标记为“永久”。此外,由于类是JVM实现的一部分而不是由应用程序创建,因此它们被视为“非堆”内存。

对于JDK8之前的HotSpot JVM,这些“永久”表示将存在于称为“永久代”的区域中。这个永久代与Java堆是连续的,并且仅限于-XX:MaxPermSize,它必须在启动JVM之前在命令行上设置,或者默认为64M(64位缩放指针为85M)。永久世代的集合将与旧一代的集合联系在一起,因此无论何时满足,都将收集永久代和旧代。您可以立即调用的一个明显问题是依赖于-XX:MaxPermSize。如果类元数据大小超出-XX:MaxPermSize的范围,则应用程序将耗尽内存并且您将遇到OOM(内存不足)错误。

随着JDK8的出现,我们不再拥有PermGen。不,元数据信息没有消失,只是它所持有的空间不再与Java堆相邻。元数据现在已经移动到本机内存到称为“Metaspace”的区域。

由于PermGen很难调整,因此转向Metaspace是必要的。元数据可能随着每个完整的垃圾收集而移动。此外,很难确定PermGen的大小,因为它的大小取决于很多因素,例如类的总数,常量池的大小,方法的大小等。

此外,HotSpot中的每个垃圾收集器都需要专门的代码来处理PermGen中的元数据。从PermGen中分离元数据不仅允许Metaspace的无缝管理,而且还允许改进,例如简化完整垃圾收集以及将来并发分配类元数据。

 

删除永久空间对最终用户意味着什么?

由于类元数据是从本机内存分配的,因此最大可用空间是可用的总系统内存。因此,您将不再遇到OOM错误,并最终可能溢出到交换空间。最终用户可以选择为类元数据限制最大可用本机空间,或者用户可以让JVM按顺序扩展本机内存以容纳类元数据。

注意:删除PermGen并不意味着您的类加载器泄漏问题已经消失。所以,是的,您仍然需要监控您的消费并相应地进行计划,因为泄漏会最终消耗您的整个本机内存,从而导致交换变得更糟。

 

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/considerations.html

类元数据
Java类在Java Hotspot VM中具有内部表示,并称为类元数据。在以前版本的Java Hotspot VM中,类元数据是在所谓的永久生成中分配的。在JDK 8中,永久代被删除类元数据在本机内存中分配。默认情况下,可用于类元数据的本机内存量是无限制的。使用该选项MaxMetaspaceSize可以为用于类元数据的本机内存量设置上限

Java Hotspot VM显式管理用于元数据的空间。从操作系统请求空间,然后分成块。类加载器从其块中为元数据分配空间(块绑定到特定的类加载器)。为类加载器卸载类时,其块将被回收以供重用或返回到操作系统。元数据使用分配的空间mmap,而不是malloc。

卸载相应的Java类时,将释放类元数据。由于垃圾收集而卸载Java类,并且可能会导致垃圾收集以卸载类并释放类元数据。当为类元数据提交的空间达到一定水平(高水位线)时,会引发垃圾收集。在垃圾收集之后,可以根据从类元数据中释放的空间量来升高或降低高水位标记。将提高高水位标记,以免过早引起另一次垃圾收集。高水位标记最初设置为命令行选项的值MetaspaceSize。这是上升或下降基础上,选择MaxMetaspaceFreeRatio和MinMetaspaceFreeRatio。如果类元数据的可用空间占类元数据总承诺空间的百分比大于MaxMetaspaceFreeRatio,则高水位标记将降低。如果小于MinMetaspaceFreeRatio,则高水位标记将被提升。

为选项指定更高的值,MetaspaceSize以避免为类元数据引发早期垃圾收集。为应用程序分配的类元数据量取决于应用程序,并且不存在用于选择的一般准则MetaspaceSize。默认大小MetaspaceSize取决于平台,范围从12 MB到大约20 MB。

 

2.设置PretenureSizeThreshold直接在年老代分配内存

XX:PretenureSizeThreshold 的意思是超过这个值的时候,对象直接在old区分配内存

默认值是0,意思是不管多大都是先在eden中分配内存。

https://www.oracle.com/technetwork/systems/index-156457.html

如果年轻一代的规模很小,并且对象的大小很大,那么现在直接在老一代中创建对象。并发收集器中的选项 - XX:PretenureSizeThreshold = <字节大小>,可以启用该选项 以指示在旧一代中直接创建的阈值。这可以有效地用于创建长期存在的高速缓存,查找表等,并且不必经历在年轻一代中创建然后被复制到老一代的促销周期。请谨慎使用此选项,因为它会降低性能而不是改进性能。默认值为0,即在旧一代中不直接创建任何对象。

 

https://stackoverflow.com/questions/35526190/automatically-promoting-an-object-to-old-generation

https://stackoverflow.com/questions/24618467/size-of-huge-objects-directly-allocated-to-old-generation

https://stackoverflow.com/questions/2129044/java-heap-terminology-young-old-and-permanent-generations

https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html

https://bugs.openjdk.java.net/browse/JDK-8050209

https://www.jianshu.com/p/f7cde625d849

参考《深入理解Java虚拟机 JVM高级特性与最佳实践.周志明著》

 

展开阅读全文

没有更多推荐了,返回首页