方法区就是自己以前理解的常量池,只不过以前理解的比较片面而已,它不是在jvm堆中的,不要被jvm 笔记7所误解。它们是有共同点的就是都是共享的。
一,网上的解释(方法区)
当jvm使用类装载器装在某个类时,它首先要定位到对应的class文件,然后读入这个class 文件, 后提取该文件的内容信息,并将这些信息存储到方法去, 后返回一个class实例。
方法区是系统分配的一个内存逻辑区域,是一块所有线程共享的内存区域,用来存储类型信息(类型信息可以理解为类的描述信息(类的全限定名,访问修饰符,字段,方法等)),方法区的大小决定了系统可以包含多少个类,如果系统类太多,方法区内存不够会导致方法区溢出,虚拟机同样会抛出内存溢出信息。方法去特点:
1.方法区是线程安全的,由于所有的线程都共享方法区,所以方法区里的数据访问必须被设计成线程安全的。例如,假如同时有两个线程都企图访问方法区中的同一个类,而这个类还没有被装入jvm,那么只允许一个线程去装在它,而其他线程必须等待。
2.方法去的大小不必是固定的,jvm可根据应用需要动态调整,同时,方法区也不一定是连续的,方法区可以在一个堆(甚至是jvm自己的堆)中自由分配。
3.方法区也可被垃圾收集,当某个类不在被使用时,jvm将卸载这个类,进行垃圾收集。
方法区存放内容:
1.类的全限定名(类的全路径名)。
2.类的直接超类的权全限定名(如果这个类是Object,则它没有超类)。
3.类的类型(类或接口)。
4.类的访问修饰符,public,abstract,final等。
5.类的直接接口全限定名的有序列表。
6.常量池(字段,方法信息,静态变量,类型引用(class))等类变量:类变量为静态变量,在方法去中有个静态去,静态去专门用来存放静态变量以及静态块。
方法区内存大小设置: jdk1.6,jdk1.7 永久区
-XX:PermSize=10M 初始化方法区大小为10M。
-XX:MaxPermSize 方法区 大内存为10M。
-XX:PrintGCDetails 打印日志详情。
jdk1.8 元数据区
-XX:MetaspaceSize=10M
-XX:MaxMetaspaceSize=10M
元数据区发生溢出,虚拟机一样抛出异常,如下: java.lang.OutOfMemoryError Metaspace
其实这里我们 主要的看得及时方法区中都有什么信息。下面我们来看一下常量池的内容。二,常量池
首先我们来看一下这段代码
String a="abc";
String b="abc"; System.out.println(a==b); 结果是true
我们可以看到a和b并不是在堆中创建的,假如在堆中创建a和b就不相等了,所以只能在常量池中创建,常量池里面对比的时候用的是hashset,这个时候,hashset是无序的,这个时候假如a=abc到了常量池中,b再进来的时候发现同样是abc就不会创建,还是原来的abc 的值。所以是true。
String a="abc";
String b=new String("abc");
System.out.println(a==b);
结果是false
这个时候的b就会在堆中创建对象,而a在常量池中肯定是不相等的。
String a="abc";
String b=new String("abc");
System.out.println(a==b.intern()); 这个时候就结果就是true了。这有事为什么呢。
这个时候就相当于把b从堆中移到常量池中去了。这个常量就叫做运行时常量。、
其实还有一种理解a和b都在队中的,只不过是指向了同一块内存区域,更倾向于这种理解。
我们来看一下运行时常量和编译时常量的区别
public static final int a = 10就是一个编译时常量,在编译后的符号中找不到a,所有对a 的引用都被替换成了20;它是不依赖于类的,在编译时就可以确定值。
public static final int b = "hello".length()就是一个运行时常量;它是依赖于类的,它的赋值会引起类的初始化。
归根结底还是javac的编译机制导致了这样的结果。当涉及到方法的调用时,必须在运行的时候才能确定结果
原文:https://www.cnblogs.com/qingruihappy/p/9691309.html