什么是方法区
jvm内存结构
是jvm定义的一种规范
hotspot中1.7的实现方式是永久代,1.8改为了元空间
元空间不在虚拟机设置的内存中,而是使用物理内存
方法区里存了什么
Person persion = new Persion();
方法区 栈 堆
方法区
- 元数据 - 本地内存的原空间 (类信息,方法)
- 常量池 - 堆中 (字符串常量池,静态变量)
常量池
- 每个class文件都有一个class常量池。
- 字面量包括:1.文本字符串 2.八种基本类型的值 3.被声明为final的常量等;
- 符号引用包括:1.类和方法的全限定名 2.字段的名称和描述符 3.方法的名称和描述符。
- JDK1.8 HotSpot 移除了永久代用元空间(Metaspace)取而代之, 这时候字符串常量池和静态变量还在堆, 运行时常量池还在方法区, 只不过方法区的实现从永久代变成了元空间(Metaspace)
如何理解方法区
《Java虚拟机规范》中明确说明:“尽管所有的方法区在逻辑上是属于堆的一部分,但一些简单的实现不会选择去进行垃圾收集或者进行压缩。”但对于HotSpotJVM而言,方法区还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。
-
方法区(Method Area)与Java堆一样,是各个线程共享的内存区域。
-
方法区在JVM启动的时候被创建,并且它的实际的物理内存空间中和Java堆区一样都可以是不连续的。
-
方法区的大小,跟堆空间一样,可以选择固定大小或者可扩展
-
方法区的大小决定可系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误:java.lang.OutOfMemoryError:PermGen space或者java.lang.OutOfMemoryError:Metaspace
-
怎么会导致方法区的OOM呢?
- 方法区主要是存储类信息,那么当加载大量的jar包:Tomcat部署的工程过多(30-50)个;大量动态的生成反射类都会造成OOM
-
-
关闭JVM会释放这个区域的内存
-
方法区的垃圾回收的目标主要包括:**常量池中废弃的常量和不再使用的类型。
如何设置方法区
JDK7及以前:叫永久代
- 通过-XX:PermSize来设置永久代初始分配空间。默认是20.75M
- -XX:MaxPermSize来设定永久代最大可分配空间。32位机器默认是64M,64位机器模式是82M。
- 当JVM加载的类信息容量超过了这个值,会报异常OutOfMemoryError:PermGen space。
JDK8及以后:叫元空间
-
元数据区大小可以使用参数
-XX:MetaspaceSize和-XX:MaxMetaspaceSize指定,替代上述原有的两个参数 -
默认值依赖于平台。windows下,-XX:MetaspaceSize是21M(近似,实际上20.75多)-XX:MaxMetaspaceSize的值是-1,即没有限制
-
与永久代不同,如果不指定大小,默认情况下,虚拟机会耗尽所有的可用系统内存.如果元数据区发生溢出,虚拟机一样会抛出异常OutOFMemoryError:Metaspace
-
-XX:MetaspaceSize:设置初始的元空间大小。对于一个64位的服务器端JVM来说,其默认的
-XX:MetaspaceSize值接近21M。这就是初始的高水位线,一旦触及这个水位线,Full GC将会被处罚并卸载没用的类(即这些类对应的类加载器不再存活),然后这个高水位线将会重置。新的高水位线的值取决于GC后释放了多少元空间。如果释放的空间不足,那么在不超过MaxMetaspaceSize时,适当提高该值。如果释放空间过多,则适当降低该值
- 如果初始化的高水位线设置过低,上述高水位线调整情况会发生很多次。通过垃圾回收器的日志可以观察到Full GC多次调用。为了避免频繁地GC,建议将
-XX:MetaspaceSize设置为一个相对较高的值。
- 如果初始化的高水位线设置过低,上述高水位线调整情况会发生很多次。通过垃圾回收器的日志可以观察到Full GC多次调用。为了避免频繁地GC,建议将
为什么要用元空间代替永久代
用元空间替换永久代的原因:
-
永久代设置空间大小是很难确定的
- 在某些场景下,比如web工程中,需要动态的加载大量的jar包和类,那么就可能会出现OOM问题。
-
对于永久代进行调优是很苦难的。
- 当永久代满,就需要发生GC,而永久代的GC可能会发生较长时间STW,那么就是损失性能的。
字符串常量池
1.为什么有字符串常量池
- 实现这种设计的一个很重要的因素是:String类型是不可变的,实例化后,不可变,就不会存在多个同样的字符串实例化后有数据冲突;
- 运行时,实例创建的全局字符串常量池中会有一张表,记录着长相持中每个唯一的字符串对象维护一个引用,当垃圾回收时,发现该字符串被引用时,就不会被回收。
2.创建流程
对于jvm底层,String str = new String("123")创建对象流程是什么?
- 在常量池中查找是否存在"123"这个字符串;若有,则返回对应的引用实例;若无,则创建对应的实例对象;
- 在堆中new一个String类型的"123"字符串对象;
- 将对象地址复制给str,然后创建一个应用。
3.几个对象
String str ="ab" + "cd"; // 1个,在常量池
String str = new String("abc"); // 2个,在堆 + 常量池
String str = new String("a" + "b");// 4个,常量池:a,b,ab 堆ab