技术文章第一时间送达!
之前学习类加载器的时候,最后放出了一张图,再来回顾一下
类加载器就是把字节码文件加载到运行时数据区里面的一个机制,加载到运行时数据区之后呢,又发生了什么?
接下来我们就来看看。这就是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 永久代已经废掉了,直接使用内存,不过多阐述,有兴趣可自行去了解。
有什么错误,或者用词不当还希望大家留言。