1、jvm内存结构
静态编译:把java源文件编译成字节码文件class,这个时候class文件以静态方式存在。
类加载器:把java字节码文件加载到内存中
方法区:将字节码放到方法区作为元数据(简单名字+描述符)。
堆:对象(类的实例)
方法区和堆:运行时数据区在所有线程间共享
虚拟机栈、本地方法栈、程序计数器:运行时数据区线程私有
2、堆
(1)对于大多数应用来说,java堆是java虚拟机所管理的内存中的最大的一块
(2)java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建
(3)此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例对在这里分配内存
new Person();
(4)如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,OutOfMemoryError异常
String [] str =new String[1000000];
如果再分配数组的内存之前将jvm的数值调小后便会发生此异常。
3、方法区
(1)存放的信息
已经被jvm加载的类的信息
常量
静态变量
即使编译器编译后的代码(JIT)
(2)JIT:热点代码编译后存储到方法区
for(int i=0;i<100;i++){ add(); }
上面的代码编译后存放起来,避免反复编译
(3)编译的过程
4、程序计数器
(1)一块较小的内存空间,它的作用是当前线程所执行的字节码行号指示器,唯一一个在jvm中没有规定任何OutOfMemoryError的区域
(2)程序寄存器存放下一条指令的地址
5、本地方法栈
为本地方法的调用,执行,退出
(1)栈是有深度的:
private Long aLong=1l; public void test(int a,int b){ aLong++; System.out.println(aLong); test(a,b); } public static void main(String[] args) { Test1 test1=new Test1(); test1.test(0,0); }
每调用一次占用一个栈帧:
栈的默认大小为5248,默认1M。
函数的调用过程:
6、虚拟机栈
分配基本类型和自定义对象的引用,用于存放,局部变量表、操作数栈、动态链接\方法的返回地址
7、堆、栈、方法区
(1)字符串相关:
String string="q"+"w"+"3";
后面的三个字符只创建了一个对象,因为存在字符串的折叠
String string=new String("hello");
当常量池中已经有了“hello”字符串后在常量池中就不必再创建了,只需在栈内存中创建一个对象即可;但是,如果在常量池中没有“hello”字符串的话,就需要创建两个字符串对象了。
(2)JVM执行流程
public class Person { private String name; public void sayhello(String name){ System.out.println("hello"+name); } public static void main(String[] args) { Person person=new Person(); person.sayhello("Tom"); } }
JVM去方法区寻找Person类信息如果我不到,Classloader加载Person类信息进入内存方法区
在堆内存中创建Person对象,并持有方法区中Person类的类型信息的引用
把person添加到执行main0方法的主线程java调用栈中,指向堆空间中的内存对象
执行person.sayHello0时,JVM根据person定位到堆空间的Person实例
根据Person实例在方法区持有的引用,定位到方法区Person类型信息,获得sayHello0字节码,执行此方法执行,打印出结果。