jvm:内存结构(堆、方法区、程序计数器、本地方法栈、虚拟机栈)

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字节码,执行此方法执行,打印出结果。

猜你喜欢

转载自www.cnblogs.com/zhai1997/p/12599306.html