方法区
方法区
和 堆
一样 都会有 OOM
我们可以动态调节 方法区
的大小,也可以给他固定的大小,并且方法去也会存在垃圾回收
。
堆栈方法区的交互关系
- 堆
- 方法对象的
- 栈
- 当前运行的方法已经方法的变量和指向堆内存的地址的服务引用
- 方法区
- 运行需要的所有的字节码文件
class Test {
public static void main(String[] args) {
TestDemo testDemo = new TestDemo();
/*
testDemo : 栈空间
TestDemo : 方法区
new TestDemo() : 堆空间
*/
}
}
class TestDemo {
private int i;
public int getI() {
return i;
}
}
方法区的理解
方法区和堆空间一样都是各个线程共享的内存区域。
关闭 JVM 就会释放这个区域的内存
设置方法区的大小和OOM
-
1.7 和 之前版本:
-XX:PermSize=大小
最小的-XX:MaxPermSize=大小
最大的
-
1.8 和以后版本:
-XX:MatespaceSize=大小
最小的-XX:MaxMatespaceSize=大小
最大的- 如歌等于 -1 就是对大小不做限制。物理内存可用的最大值
如何解决和处理 方法区的OOM
可以导出堆的快照,然后用第三方工具分析(eclipse memory analyzer; JProfile; jvisvm),然后查出是 内存泄漏
还是 内存溢出
。
- 如果是内存溢出,则需要看一下堆的对象和引用链中的 GC ROOT 是否存在不被使用但是对象没有被 GC 回收的事情,如果存在则需要处理掉这些。
- 如果是内存溢出。看一下是堆溢出还是栈溢出可以适当调节堆栈内存
注:内存溢出:存在不被代码使用已经废弃的对象,但是没有被垃圾回收器回收的对象
方法区的内部结构
jdk 1.8 之前是 永久代
1.8 以后就是 元空间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mo7BDXMh-1624614944587)(C:\Users\L\AppData\Roaming\Typora\typora-user-images\image-20210614163815854.png)]
字符串常量池再不同的 jdk 版本是不一样的
1.7 及之前,是存在方法区的,1.8 及以后是存在堆空间的。
方法区使用举例
方法区的演进细节
1、描述
只有 Hosport 才有永久代,Ibm 的 J9 和 jrockit 是没有永久代的是有元空间
2、Hosport 永久代的变化
jdk 版本 | Jdk 各个版本的演进变化 |
---|---|
1.6 及 之前 | 有永久代(permanent generation),静态变量存储在永久代上面。 |
1.7 | 有永久代,但已经逐步去永久代,字符串常量池、静态变量移除,保存在堆中。 |
1.8 及 以后 | 没有永久代,类型信息,字段,方法,常量保存在本地元空间,但字符串常量池、静态变量仍在堆。 |
3、替换的原因
- 在某些情况下,如果 Perm 区域 动态加载类太多容易产生 OOM 。
- 比如在世家的 web 场景下,因为功能点比较多,在运行过程中。要不断地的动态加载很多的类,经常出现致命错误(OOM)
Exception in thread 'dubbo client xxx connector' java.lang.OutOfMemaryError:PermGen
- 而
永久代
空间和元空间
最大的区别在于:- 元空间:
- 不是虚拟内存的映射而是机器的真实内存,默认情况下只受本地内存的限制
- 永久代:
- 虚拟内存的映射
- 元空间:
- 比如在世家的 web 场景下,因为功能点比较多,在运行过程中。要不断地的动态加载很多的类,经常出现致命错误(OOM)
方法区的大小和 OOM
元空间:设置方法区的初始值和最大值。默认 方法区最大为 -1 最小的为 21M
设置方式:-XX:MatespaceSize=size 初始值 -XX:MaxMatespaceSzie=size 最大值
一般我们不会修改最大值但是我们会一定程度的修改最小的值,因为每一次最小的值的提升必定伴随着一个 FULL GC。而 FULL GC 往往也会伴随着系统性能的消耗, SWT。
StringTable 为什么会被放到堆空间
因为在永久代里面的数据只有在 FULL GC 的时候才回去只写哪个回收,所以回收效率很低。而 FULL GC 只有在永久代内存不足、堆内存不足,才会执行。而我们开发当中会创建很多的字符串。会少效率低,导致永久代的内存不足。放到堆里面可以提升回收效率。
只有new的对象都会放在堆内存。
JHSDB
JDK 9 以后系统附带的工具
方法区的垃圾回收
只会有 FULL GC
。
**常量池的回收,**只要常量池没有被任何地方引用,就可以被回收。
类的回收,必须同时满足三个条件才可以进行回收
1:class类不被使用,且他的子类也不会被使用
2:classloader 被回收
3:该类对应的 Class 的类没有被任何地方引用(避免有反射的类的存在)