JVM你必需知道的基础

来源:互联网 发布:种植药材前景 知乎 编辑:程序博客网 时间:2024/05/22 17:35

引言:

昨天写了一篇关于java中对象生死的问题,主要目的是想让大家感觉一下我所感受到的东西:其实jvm也非常有意思,甚至对象的创建与消亡都可能触动你的心弦。所以,今天我打算深入学习一下jvm必要基础(上一篇博文也略有涉及),主要介绍一下JVM运行时各个区域的区别和作用,本来想罗列一张表的,但是篇幅会很丑,所以我就拆开一部分一部分来说。同样的,我会先在技术点写一些博文涉及到以外的知识点,方便大家理解。笔者目前整理的一些blog针对面试都是超高频出现的。大家可以点击链接:http://blog.csdn.net/u012403290

技术点:

1、栈:

又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。 –百度词条

上面我引用了百度词条的解释帮助大家理解,其实栈就类似于往一根底端封闭的管子中放弹珠,放进去的过程叫压栈,拿出来最上面那个叫出栈。但是如果你要拿管子最底下那个弹珠呢?你就需要一颗颗把上面的拿出来,直到拿到最下面那颗。这就是栈的先进后出的规则。

2、栈溢出与内存溢出:

栈溢出:线程请求的栈深度大于虚拟机所允许的深度就会产生栈溢出。这样说可能不好理解,换种说法就是,从技术点1可以知道栈是一种线性表,如果栈内存容量很小,那么我却定义了大量的需要被栈存储的数据,那么这个线性表的长度就不够用了就会导致栈溢出。
内存溢出:如果没有获得足够的内存那么就会导致内存溢出。

3、线程隔离与现场共享:

简单来说就是线程与线程之间存不存在共享资源或者说线程之间是否相互影响,如果一个线程的执行与其他线程互不影响,独立存储,那么这个线程就是隔离的。

4、java的native方法:

简单来说就是java调用了不是java代码的接口。因为java并不是万能的,必要时候可能需要别的语言合并开发,那么java在调取别的语言的接口的时候就是执行了native方法。

5、新生代,老年代,永久代:

简单来说,新生代对象是“朝生夕死“的,GC回收很快;老年代的话回收频率就会变得相对较慢,而且是要指定的一些GC过程才会进入回收。永久代(一般指方法区),基本不会回收。

JVM必要基础

现在进入核心部分,我们以类比的方式进行阐述,请各位注意比较。现引入一张昨天的图:

这里写图片描述

比较模块主要有:①作用,②消费内存,③线程关系,④异常,⑤所包含重要模块

1、程序计数器:①作用:作为行号指示器,用于选取下一条需要执行的字节码指令。②消费内存:确定大小,很小的内存空间。③线程关系:线程隔离。④异常:无。⑤所包含重要模块:无。

2、java虚拟机栈:①作用:存放在编译期间已知的各种基本数据类型(boolean,byte,char,int,short,float,long,double),对象的引用和指向一条字节码指令的地址。②消费内存:编译期间确定内存大小。③线程关系:线程隔离。④异常:栈溢出与内存溢出。⑤所包含重要模块:栈帧,局部变量表。

注意:我这里对虚拟机栈做一点更清楚的解释,它们的包含关系是:虚拟机栈>栈帧>局部变量表。而我们一般来说的栈,其实是指局部变量表,因为局部变量表用语存储对象引用。每一个方法都会有一个栈帧,这个栈帧就是我前面的比喻“弹珠“。每个方法在执行过程中都会创建一个栈帧,方法调用到执行完成就是栈帧在虚拟机栈中从入栈到出栈的过程。

3、本地方法栈:①作用:类似于java的虚拟机栈,不过它主要用于执行Native方法。②消费内存:编译期间确定内存大小。③线程关系:线程隔离。④异常:栈溢出与内存溢出。⑤所包含重要模块:无。

4、java堆(新生代与老年代):①作用:存储对象实例。②消费内存:运行期间动态分配。③线程关系:线程共享。④异常:内存溢出。⑤所包含重要模块:无。

注意:这里说明一个很乱的问题,对象一定是在堆上分配么?这个答案是否定的,因为随着计算机技术的发展,在栈上分配对象也都可以。我举个栗子,有个技术叫逃逸分析:它是指当一个对象在方法中被定义之后,它如果被外部方法引用,则称为逃逸。那么,如果这个对象不发生逃逸,它的生命周期就和它的引用存活一样长的时间,在引用出栈的时候就可以顺带销毁对象了,这样以来既可以加快访问速度,也可以减小GC机制的压力。是不是很奇妙?

5、方法区(永久代):①作用:存储已被加载的类信息,常量,静态变量和即时编译后的代码。②消费内存:运行期间动态分配。③线程关系:线程共享。④异常:内存溢出。⑤所包含重要模块:运行时常量池。

下面这张图大致说明访问对象的过程和各个模块之间的调度关系(不止如下一种访问方式):

这里写图片描述

注意:常量池用于存放编译期的各种字面量和符号引用。为什么要放到常量池呢?因为它就和我们现在的缓存类似,直接可以通过常量池读取,提升系统速度和性能。比如我们经常使用的String str=“hello”,这个hello就是放在常量池中。

如果博文存在什么问题,或者有什么想法,可以联系我呀,下面是我的微信二维码:

这里写图片描述

1 0
原创粉丝点击