Person类的.classs信息放在方法区
penson变量存放在栈局部变量表
真正的对象放在Java堆
在person对象中有个指针指向方法区中的person类型数据,表明这个person对象是用方法区中的Person类new出来的
方法区看做是一块独立于堆的内存空间
方法区主要存放Class,类只能加载一次
物理上内存空间可以是不连续的
方法区大小决定可以保存多少个类
OOM实例:加载大量jar包 tomcat部署的工程多
关闭JVM就会释放这个区域的内存
JDK7及以前 习惯上称为永久代 JDK8被元空间取代(方法区类比为接口,永久代或元空间类比为实现类)
永久代在虚拟机设置的内存中而元空间使用本地内存
元空间元数据区 使用参数-XX:(Max)MetaspacesSize
默认值 windows 约21M, MAX 值是 -1 没有上限
初始高水平线21M 一旦触及,FullGC会被触发并卸载没用的类(即类对应的类加载器不在存活),然后这个高水位线将会重置,新的高水位线取决于GC后释放了多少元空间,如果释放的空间不足,不超过Max 适当提高水位线,如果释放空间过多则降低
如果初始设置过低 ,上述调整情况发生多次,通过GC日志可以观察到FullGC多次调用,为避免频繁GC 建议将设置一个相对较高的值
如何解决堆OOM先简单了解
一般手段是确认内存中的对象是否是必要的,是内存泄露还是内存溢出
内存泄漏 ,对象不会在使用但还是有引用指向,导致不会被回收
如果是内存泄露 可进一步查看GC Roots引用链,根据泄露对象找路径,定位泄露代码的位置
如果对象都确实还必须,应当检查参数与物理内存对比看是否还能调大,从代码上检查是否存在某些对象生命周期过长的情况,尝试减少程序运行期的内存消耗
方法区存储什么?《深入理解Java虚拟机》 书中对方法区描述:已被加载的类型信息,常量,静态变量,即时编译器后的代码缓存等
non-final 类型的类变量
全局常量 static final 在编译时就会被分配(写死在字节码文件)
运行时常量池VS常量池运行时常量池在方法区内部
字节码内部包含常量池
常量池一个有效的字节码文件除了包含类的版本信息,字段,方法以及接口等描述符信息外,还包含常量池表,包括各种字面量和对类型,属性,方法的符号引用
为什么需要尽量只保存一份数据实体,用数据的时候去通过引用去使用数据,这是一种节约空间的思想,常量池可以看做是一张表,虚拟机根据这张表去找到要执行的类名,方法名,参数类型,字面量等类型,是符号地址
有啥数量值
字符串值
类引用
字段引用
方法引用
1,常量池加载到方法区后的名称
2,池中的数据项像数组一样通过索引访问
3,包含多种不同的常量,包括编译器就已经明确的数值字面两字面来那个,也包括4,到运行期解析后才能获得的方法或者字段引用,此时不再是常量池的符号地址了,这里换为真实地址。
5,具备动态性
6,当创建类或接口的运行时常量池时,可能会OOM
通过调用方法的函数去调用方法(需要类已加载,未加载则执行加载,将常量池符号引用(字面量)转换为直接引用)根据描述创建新栈帧
方法区演进细节
JDK6 静态变量 StringTable 和在方法区
JDK7 ~ 在堆
JDK8 方法区移到本地内存
永久代在虚拟机内部,为永久代设置空间大小是很难确定的如果动态加载类过多会产生Perm的OOM,而元空间使用本地内存,大小仅受本地内存限制
对永久代调优很困难,垃圾回收:废弃的常量和不在使用的类,常量(主要字面量和符号引用)的回收类似对象的回收
方法区的调优主要是为了降低FUllGC
1 类型卸载条件苛刻
该类所有的实例都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例。
加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如OSGi、JSP的重加载等,否则通常是很难达成的。
该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
2、Java虚拟机被允许对满足上述三个条件的无用类进行回收,这里说的仅仅是“被允许”,而并不是和对象一样,没有引用了就必然会回收。关于是否要对类型进行回收,HotSpot虚拟机提供了-Xnoclassgc参数进行控制,还可以使用-verbose:class 以及 -XX:+TraceClass-Loading、-XX:+TraceClassUnLoading查看类加载和卸载信息
3、在大量使用反射、动态代理、CGLib等字节码框架,动态生成JSP以及OSGi这类频繁自定义类加载器的场景中,通常都需要Java虚拟机具备类型卸载的能力,以保证不会对方法区造成过大的内存压力。
StringTable为什么放到了堆?因为永久代回收效率很低,在FullGC的时候才会执行永久代的垃圾回收,而FullGC是老年代,永久代不足时才会触发,在开发中会有大量的字符串被创建,导致永久代内存不足,放到堆里能及时回收
静态变量JDK7及其以后把静态变量与类型在Java语言一端的映射Class对象存放在一起,存储在Java堆中



