方法区
- 方法区又称为静态区,和堆一样,被所有的线程共享。存储了跟类结构相关的信息,Java类中的成员变量和方法都会在这里。
- 在虚拟机启动时创建
- 逻辑上是堆的组成部分
jdk7以前,方法区的实现是永久代,jdk8开始方法区的实现使用元空间取代了永久代。
方法区里面还有一个而常量池,顾名思义是存放常量的,比如:
private final int a = 1;
像这种它被final修饰了之后变成了常量,所以1这个值也就存放在了方法区的常量池中,
再来个栗子:
String str1 = "qwer";
String str2 = new String("qwer");
第一句:在栈中创建了str1,在方法区存了字符串”qwer“,
第二句:先去方法区找有没有字符串”qwer“,然后再去堆里new了个String实例,再栈中创建了str2,这样串起来这三个。
String s1 = "a";
String s2 = "b";
String s3 = "ab";
String s4 = s1+s2; //相当于 StringBuilder.append("a")+append("b").toString() 得到的结果再 return new String("ab")
//"ab"没有放到常量池中,因为它是被动态拼接的,所以它只存在于堆中(要想放入常量池中,实用s4.intren()方法,会返回一个对象)
String s5 = "a"+ "b"; //它直接去常量池中找“ab”字符串,也就是 s5 == s3
/
String s = new String("a")+new String("b");
String ss = "ab";
System.out.println(ss == s); //false
String ss = "ab";
String s = new String("a")+new String("b");
System.out.println(ss == s); //true
本地方法栈
- java间接通过本地方法栈调用底层的代码
- 本地方法可以通过本地方法接口来访问虚拟机内部的运行时数据区
- 在Hotspot JVM中,直接将本地方法栈和虚拟机栈合二为一
怎么看自己的是什么虚拟机?
native
凡是用了这个修饰的,java已经够不着了,native修饰的方法在本地方法栈,它会通过JNI(Java native interface)调用本地方法库,那里面都是c或者c++实现的方法,也就是通过这些接口来调用c代码。(像Object类的方法中有很多都是具有native修饰的方法,notify(),clone(),hashCode()等)
栈
- 栈存取速度比堆要快,仅次于寄存器。
- 栈中主要存放一些基本类型的变量数据和一些对象引用。
- 在栈中数据是可以共享的。
举个栗子:
int a = 5;
int b = 5;
首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有5这个值,如果没找到,就5存放进来,然后将a指向5。接着执行int b = 5;在创建完b的引用变量后,因为在栈中已经有5这个值,所以可以直接b指向5。这样,a跟b同时指向5,可以理解成它们共用了5,如果改变了a的值(执行 a = 6;)b是不会跟着改变的,它会找栈中是否有6,如果有将a指向6,没有就将6存入栈中,再将a指向6。
栈中的那些引用变量的存活是根据它在代码里的作用域来的
举个栗子:
public static void test01(){
if (1==1){
int temp = 10000;
System.out.println("呃呃呃呃");
}
}
在if里面定义了temp局部变量,等if执行结束后,temp自然就没了。
堆
- 堆用来存放由new创建的对象和数组。 在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。
- 堆是线程共享的,所以要考虑线程安全问题。
举个栗子:
Student stu = new Student();
这new了一个Student对象,首先先在堆内存开辟一块内存区间,存放 Student实例对象,然后将成员变量的地址和成员方法的地址放在 new 实例中,然后在栈中定义了一个用于指向 Student 实例的变量 stu(栈中有stu并且指向堆中的Student实例)。
堆中的东西所占的内存不会被释放,就是当栈中没有引用变量指向它的时候,它就被当成了垃圾,会被Java的垃圾回收器回收掉。
程序计数器
- 用于存储下一条指令的地址
- 它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。并且字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令
- 程序计数器是一块很小的内存空间,几乎可以忽略不记,它也是运行速度最快的存储区域
双亲委派机制
- 类加载器收到类加载的请求
- 将这个请求向上委托给父加载器去完成(应用程序加载器向上交给扩展类再向上交给根加载器,都向让父类去加载)
- 根加载器能加载就结束,否则让子类加载
如果都没有则报错 :ClassNotFound
好处:一定程度上防止了危险代码的植入