JVM的特性,通过代码来揭秘运行时数据区

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lkp1603645756/article/details/83867571

运行时数据区

之前学习类加载器的时候,最后放出了一张图,再来回顾一下

类加载器就是把字节码文件加载到运行时数据区里面的一个机制,加载到运行时数据区之后呢,又发生了什么?

接下来我们就来看看。这就是JVM运行时数据区:

运行时数据区分为:方法去、堆、虚拟机栈、本地方法栈、程序计数器。

而黄色区,会被称为栈。

堆和栈的根本作用,就是用来存放数据用的。

先上一段代码:

/**
 * 作者:LKP
 * 时间:2018/11/7
 */
class Person{
    String name = new String("1234");

    public Person(String name) {
        this.name = name;
    }

    public void sayHello(){
        System.out.println("hello:"+name);
    }
}

public class AppTest {

    public static void main(String[] args) {
        Person person = new Person("张三");
        person.sayHello();
    }

}

后面的分析都是建立在这个AppTest类的。

堆:

堆是用来干嘛的?

就好比前面的程序,new Person("张三"); 它存储的地方就是在堆里面。

什么是OutOfMemoryError异常,可能有些人没有接触过,我也是再一次面试当中遇到的,之后去查阅过相关资料。

现在我们来模拟一下OutOfMemoryError异常:

/**
 * 作者:LKP
 * 时间:2018/11/7
 */
public class HeapOOM {
    //-Xms64m -Xmx128m
    public static void main(String[] args) {
        String[] str = new String[400000000];
        System.out.println(str.length);
    }
}

启动参数设置为:-Xms64m -Xmx128m,然后运行:

这种异常就是OutOfMemoryError异常,内存溢出了,造成的原因很多种,有兴趣的小伙伴可以去了解一下。

方法区:

之前说到了类加载加载,并且执行,我们怎么样执行呢?这就跟方法区有关系了。

类信息:它是对一个类的描述

上面两条sql语句一样,第一条是它的表结构,这些就是表结构的信息。类信息(MetaInfo)就是元数据,描述我们一个类的信息的。

运行时常量池:它的作用是存放我们一些常量和静态变量的

比如:

静态变量:static int NAME = "张三";

常量:final .....

这些都是存放在运行时常量池的。

编译器有两个:一个是静态编译,一个是JIT。

JIT编译:就是运行编译。

静态编译:java编译成class文件

为什么要有JIT编译呢?那肯定是有它好处的:

看一下这段代码,他是热点代码,就是需要频繁去执行的

为了效率,JIT编译会把字节码编译为机器执行码,这样速度就大大提高了。

JIT的目的,就是把字节码>>>机器执行码,把它存放在方法区里面。

方法区呢,就是存放方法的地方,不过为了区分不同类的方法,也需要把类信息也存储进去,这样才能区分不同类的相同方法。

程序计数器:

什么是程序计数器?

程序计数器它就是让我们程序按照我们的指定指令执行的步骤,我们的步骤放到一个区域里面,程序计数器就按照第一步干什么,第二步干什么来执行。

栈:

什么是栈呢?先看看这张图

为了更好的进行理解,我们先来写个递归:

/**
 * 作者:LKP
 * 时间:2018/11/8
 */
public class Digui {

    private Long i = 0l;

    public void test(int a, double d) {
        i++;
        System.out.println("=====>" + i);
        test(a, d);
    }

    public static void main(String[] args) {
        Digui app = new Digui();
        app.test(0, 0.0d);
    }

}

执行一下:

报错了(StackOverflowError)。为什么报错呢?

StackOverflowError异常代表的是,当栈深度超过虚拟机分配给线程的栈大小时就会出现此error。

所以栈和程序运行有关:

栈概念:先进后出的原则,刚刚出现StackOverflowError的异常,证明栈是有数量限制的。

每个栈帧里面存储的又是什么呢?

局部变量表又是什么?

main函数一般都是主线程,步骤1产生的就是局部变量表。

那为什么又要压栈呢?

看一下步骤2,因为当运行main线程的时候,add线程还没有产生。当运行add的时候会把它放在main上面,为什么这样,这就和等下弹栈有关系了。

步骤2返回C就是最关键的,它就是弹栈过程,弹出的这个数据机构(add线程)就消失了,什么都没有了,包括局部变量什么的。

步骤3是返回到main线程去了。

为什么用栈不用队列呢?原因很简答,因为弹栈压栈都是最简单的,而队列则需要去查找。

来看看JVM中堆、栈和方法区这三者的联系。

局部变量表可以存放八大数据基本类型,再加上一种引用reference(引用就是一个地址,指向堆、常量池的地址)

回顾一开始出现的程序,结合来理解这三者的关系。

看完这篇文章,相信你对数据运行区的了解加深了很多。

最后再来看一下JVM内存区域:

1.8 永久代已经废掉了,直接使用内存,不过多阐述,有兴趣可自行去了解。

有什么错误,或者用词不当还希望大家留言。

猜你喜欢

转载自blog.csdn.net/lkp1603645756/article/details/83867571
今日推荐