java permgen_Java PermGen哪里去了?

java permgen

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

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

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

Trivia:在JDK7之前,对于HotSpot JVM,在永久代(也称为PermGen)中还保留有interned-strings,从而导致大量性能问题和OOM错误。 有关从PermGen中删除插入字符串的更多信息,请参见此处

再见,再见PermGen,您好,Metaspace!

随着JDK8的到来,我们不再拥有PermGen。 不,元数据信息并没有消失,只是保存它的空间不再与Java堆相邻。 元数据现在已移至本机内存中的“ Metaspace”区域。

由于PermGen确实很难调整,因此必须迁移到Metaspace。 元数据可能会随每个完整的垃圾回收一起移动。 而且,由于对PermGen的大小取决于很多因素,例如类的总数,常量池的大小,方法的大小等,因此很难确定PermGen的大小。

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

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

由于类元数据是在本机内存中分配的,因此最大可用空间是总可用系统内存。 因此,您将不再遇到OOM错误,并可能最终溢出到交换空间中。 最终用户可以选择为类元数据设置最大可用本机空间,或者用户可以让JVM依次增加本机内存以容纳类元数据。

注意 :删除PermGen并不意味着您的类加载器泄漏问题已解决。 因此,是的,您仍然必须监视您的使用情况并做出相应的计划,因为泄漏最终会消耗掉您的整个本机内存,从而导致交换只会变得更糟。

进入元空间及其分配:

现在,Metaspace VM使用内存管理技术来管理Metaspace。 因此,将工作从不同的垃圾收集器移动到仅一个在Metaspace中以C ++执行其所有工作的Metaspace VM。 Metaspace背后的主题很简单,就是类及其元数据的生存期与类加载器的生存期相匹配。 也就是说,只要类加载器处于活动状态,元数据就会在Metaspace中保持活动状态,并且无法释放。

我们一直在宽松地使用“ Metaspace”一词。 更正式地说,每个类加载器的存储区域称为“元空间”。 这些元空间统称为“元空间”。 每个类加载器的元空间回收只能在其类加载器不再活动并且被垃圾收集器报告为已死亡之后才发生。 这些元空间中没有重定位或压缩。 但是会扫描元数据以获取Java引用。

Metaspace VM通过使用分块分配器来管理Metaspace分配。 分块大小取决于类加载器的类型。 有一个全球免费的块列表。 每当类加载器需要一个块时,它都会将其从全局列表中删除并维护自己的块列表。 当任何类加载器死亡时,其块将被释放,并返回到全局空闲列表。 块进一步分为块,每个块保存一个元数据单元。 来自块的块分配是线性的(指针碰撞)。 这些块是在内存映射(映射)空间之外分配的。 有这样的全局虚拟映射空间的链接列表,只要清空任何虚拟空间,它就会返回操作系统。

上图显示了在映射的虚拟空间中具有元块的元空间分配。 类加载器1和3描述了反射或匿名类加载器,它们使用“专用”块大小。 类加载器2和4可以根据这些加载器中项目的数量采用较小或中等的块大小。

元空间调整和工具:

如前所述,Metaspace VM将管理Metaspace的增长。 但是在某些情况下,您可能希望通过在命令行上显式设置-XX:MaxMetaspaceSize来限制增长。 默认情况下,–XX:MaxMetaspaceSize没有限制,因此从技术上讲,元空间的大小可能会增加到交换空间中,您将开始遇到本机分配失败的问题。

对于64位服务器类JVM,–XX:MetaspaceSize的默认/初始值为21MB。 这是最初的高水位线。 一旦达到此水印,就会触发完整的垃圾回收以卸载类(当它们的类加载器不再处于活动状态时),并重置高水印。 高水印的新值取决于释放的元空间的数量。 如果没有足够的空间被释放,则高水位线上升; 如果释放了太多空间,则高水位线将下降。 如果初始水印太低,将重复多次。 您将能够在垃圾收集器日志中可视化重复的完整垃圾收集。 在这种情况下,建议在命令行上将–XX:MetaspaceSize设置为较高的值,以避免初始垃圾回收。

在后续收集之后,Metaspace VM将自动调整您的高水位标记,以便将下一个Metaspace垃圾收集进一步推送出去。

还有两个选项:‑XX:MinMetaspaceFreeRatio和‑XX:MaxMetaspaceFreeRatio。 这些类似于GC FreeRatio参数,也可以在命令行上进行设置。

修改了一些工具以帮助获取有关元空间的更多信息,这些工具在此处列出:

  • jmap –clstats <PID> :打印类加载器统计信息。 (现在,它取代了–permstat,后者用于在JDK8之前为JVM打印类加载器统计信息)。 运行DaCapo的Avrora基准测试时的输出示例:
$ jmap -clstats <PID>
Attaching to process ID 6476, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.5-b02
finding class loader instances ..done.
computing per loader stat ..done.
please wait.. computing liveness.liveness analysis may be inaccurate ...
class_loader classes      bytes parent_loader     alive? type 

<bootstrap>     655  1222734     null      live   <internal>
0x000000074004a6c0    0    0    0x000000074004a708    dead      java/util/ResourceBundle$RBClassLoader@0x00000007c0053e20
0x000000074004a760    0    0      null      dead      sun/misc/Launcher$ExtClassLoader@0x00000007c002d248
0x00000007401189c8     1     1471 0x00000007400752f8    dead      sun/reflect/DelegatingClassLoader@0x00000007c0009870
0x000000074004a708    116   316053    0x000000074004a760   dead      sun/misc/Launcher$AppClassLoader@0x00000007c0038190
0x00000007400752f8    538  773854    0x000000074004a708   dead      org/dacapo/harness/DacapoClassLoader@0x00000007c00638b0
total = 6      1310   2314112           N/A       alive=1, dead=5     N/A
  • jstat –gc <LVMID> :现在显示元空间信息,如以下示例所示:

  • jcmd <PID> GC.class_stats:这是一个新的诊断命令,使最终用户能够连接到实时JVM并转储Java类元数据的详细直方图。

注意 :使用JDK8 build 13,必须使用‑XX:+ UnlockDiagnosticVMOptions启动Java。

$ jcmd <PID> help GC.class_stats
9522:
GC.class_stats
Provide statistics about Java class meta data. Requires -XX:+UnlockDiagnosticVMOptions.

Impact: High: Depends on Java heap size and content. 

Syntax : GC.class_stats [options] [<columns>] 

Arguments:
     columns : [optional] Comma-separated list of all the columns to show. If not specified, the following columns are shown: InstBytes,KlassBytes,CpAll,annotations,MethodCount,Bytecodes,MethodAll,ROAll,RWAll,Total (STRING, no default value)

Options: (options must be specified using the <key> or <key>=<value> syntax)
     -all : [optional] Show all columns (BOOLEAN, false)
     -csv : [optional] Print in CSV (comma-separated values) format for spreadsheets (BOOLEAN, false)
     -help : [optional] Show meaning of all the columns (BOOLEAN, false)

注意 :有关列的更多信息,请参见此处

输出示例:

$ jcmd <PID> GC.class_stats 

7140:
Index Super InstBytes KlassBytes annotations   CpAll MethodCount Bytecodes MethodAll   ROAll   RWAll   Total ClassName
    1    -1    426416        480           0       0           0         0         0      24     576     600 [C
    2    -1    290136        480           0       0           0         0         0      40     576     616 [Lavrora.arch.legacy.LegacyInstr;
    3    -1    269840        480           0       0           0         0         0      24     576     600 [B
    4    43    137856        648           0   19248         129      4886     25288   16368   30568   46936 java.lang.Class
    5    43    136968        624           0    8760          94      4570     33616   12072   32000   44072 java.lang.String
    6    43     75872        560           0    1296           7       149      1400     880    2680    3560 java.util.HashMap$Node
    7   836     57408        608           0     720           3        69      1480     528    2488    3016 avrora.sim.util.MulticastFSMProbe
    8    43     55488        504           0     680           1        31       440     280    1536    1816 avrora.sim.FiniteStateMachine$State
    9    -1     53712        480           0       0           0         0         0      24     576     600 [Ljava.lang.Object;
   10    -1     49424        480           0       0           0         0         0      24     576     600 [I
   11    -1     49248        480           0       0           0         0         0      24     576     600 [Lavrora.sim.platform.ExternalFlash$Page;
   12    -1     24400        480           0       0           0         0         0      32     576     608 [Ljava.util.HashMap$Node;
   13   394     21408        520           0     600           3        33      1216     432    2080    2512 avrora.sim.AtmelInterpreter$IORegBehavior
   14   727     19800        672           0     968           4        71      1240     664    2472    3136 avrora.arch.legacy.LegacyInstr$MOVW
…<snipped>
…<snipped>
1299  1300         0        608           0     256           1         5       152     104    1024    1128 sun.util.resources.LocaleNamesBundle
 1300  1098         0        608           0    1744          10       290      1808    1176    3208    4384 sun.util.resources.OpenListResourceBundle
 1301  1098         0        616           0    2184          12       395      2200    1480    3800    5280 sun.util.resources.ParallelListResourceBundle
              2244312     794288        2024 2260976       12801    561882   3135144 1906688 4684704 6591392 Total
                34.0%      12.1%        0.0%   34.3%           -      8.5%     47.6%   28.9%   71.1%  100.0%
Index Super InstBytes KlassBytes annotations   CpAll MethodCount Bytecodes MethodAll   ROAll   RWAll   Total ClassName

目前的问题:

如前所述,Metaspace VM采用了分块分配器。 根据类加载器的类型,有多个块大小。 同样,类项目本身的大小也不固定,因此有可能空闲块的大小可能与类项目所需的块的大小不同。 所有这些都可能导致碎片化。 Metaspace VM尚未使用压缩,因此,碎片化是当前的主要问题。

翻译自: https://www.infoq.com/articles/Java-PERMGEN-Removed/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

java permgen

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值