③. 方法区的演进细节
①. Jdk 1.6 及之前:有永久代,静态变量、字符串常量池1.6在方法区
②. Jdk 1.7 :有永久代,但已经逐步 " 去永久代 ",字符串常量池、静态变量移除,保存在堆中
③. jdk 1.8 及之后: 无永久代,常量池1.8在元空间。但静态变量、字符串常量池仍在堆中
④. 为什么要用元空间取代永久代
永久代设置空间大小是很难确定的
(①. 永久代参数设置过小,在某些场景下,如果动态加载的类过多,容易产生Perm区的OOM,
比如某个实际Web工程中,因为功能点比较多,在运行过程中,要不断动态加载很多类,经常出现致命错误
②. 永久代参数设置过大,导致空间浪费
③. 默认情况下,元空间的大小受本地内存限制)
对永久代进行调优是很困难的
(方法区的垃圾收集主要回收两部分:常量池中废弃的常量和不再使用的类型,而不再使用的类或类的加载器回收比较复杂,full gc 的时间长)
⑤. StringTable为什么要调整
jdk7中将StringTable放到了堆空间中。因为永久代的回收效率很低,在full gc的时候才能触发。而full gc是老年代的空间不足、永久代不足才会触发
这就导致StringTable回收效率不高,而我们开发中会有大量的字符串被创建,回收效率低,导致永久代内存不足,放到堆里,能及时回收内存
④. 设置方法区大小
- ①. jdk7及以前:
- -XX:PermSize=100m(默认值是20.75M)
- -XX:MaxPermSize=100m(32位机器默认是64M,64位机器模式是82M)
- 图解:
- ②. jdk1.8及以后
- -XX:MetaspaceSize=100m(windows下,默认约等于21M)
- -XX:MaxMetaspaceSize=100m(默认是-1,即没有限制)
⑤. 常量池的理解
①. 常量池,可以看做是一张表,虚拟机指令根据这张常量表找到要执行的类名,方法名,参数类型、字面量等信息
Constant pool: #1 = Methodref #7.#23 // java/lang/Object."<init>":()V #2 = Methodref #24.#25 // com/xiaozhi/heap/Order.hello:()V #3 = Fieldref #26.#27 // java/lang/System.out:Ljava/io/PrintStream; #4 = Fieldref #24.#28 // com/xiaozhi/heap/Order.count:I #5 = Methodref #29.#30 // java/io/PrintStream.println:(I)V
②. 一个有效的字节码文件中除了包含类的版本信息、字段、方法以及接口等描述信息外,还包含一项信息那就是常量池表(Constant Poo1 Table),包括各种字面量和对类型域和方法的符号引用。
③. 个 java 源文件中的类、接口,编译后产生一个字节码文件。而 Java 中的字节码需要数据支持,通常这种数据会很大以至于不能直接存到字节码里,换另一种方式,可以存到常量池这个字节码包含了指向常量池的引用。在动态链接的时候会用到运行时常量池
④. 比如如下代码,虽然只有 194 字节,但是里面却使用了 string、System、Printstream 及 Object 等结构。这里代码量其实已经很小了。如果代码多,引用到的结构会更多!
Public class Simpleclass { public void sayhelloo() { System.out.Println (hello) } }