为什么有时候学着学着会突然之间觉得一切度是那么无趣,男的每个月也有那么几天难道?哈哈,不然是什么,我还是要坚持,可以做少一点,但是不能什么度不做。总会过去的,加油 --WH
一、运行时数据区
什么叫运行时数据区呢,看下图就知道了,今天的重点就围绕这张图讲。
1、程序计数器(寄存器)
当前线程所执行的字节码行号指示器
字节码解释器工作依赖计数器控制完成
通过执行线程行号记录,让线程轮流切换各条线程之间计数器互不影响
线程私有,生命周期与线程相同,随JVM启动而生,JVM关闭而死
线程执行Java方法时,记录其正在执行的虚拟机字节码指令地址
线程执行Nativan方法时,计数器记录为空(Undefined)
唯一在Java虚拟机规范中没有规定任何OutOfMemoryError情况区域
在这其中,很多不理解的没关系,我们学过多线程,有两个线程,其中一个线程可以暂停使用,让其他线程运行,然后等自己获得cpu资源时,又能从暂停的地方开始运行,那么为什么能够记住暂停的位置的,这就依靠了程序计数器, 通过这个例子,大概了解一下程序计数器的功能。
2、本地方法栈
不知道大家看过源码没有,看过的都应该知道,很多的算法或者一个功能的实现,都被java封装到了本地方法中,程序直接通过调用本地的方法就行了,本地方法栈就是用来存放这种方法的,实现该功能的代码可能是C也可能是C++,反正不一定就是java实现的。
上面两个不是我们所要学习的重点,接下来三个才是重点。
3、虚拟机栈
这个大家都应该有所了解,现在来细讲它,虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用来存放存储局部变量表、操作数表、动态连接、方法出口等信息,每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。 这个话怎么理解呢?比如执行一个类(类中有main方法)时,执行到main方法,就会把为main方法创建一个栈帧,然后在加到虚拟机栈中,栈帧中会存放这main方法中的各种局部变量,对象引用等东西。如图
当在main方法中调用别的方法时,就会有另一个方法的栈帧入虚拟机栈,当该方法调用完了之后,弹栈,然后main方法处于栈顶,就继续执行,直到结束,然后main方法栈帧也弹栈,程序就结束了。总之虚拟机栈中就是有很多个栈帧的入栈出栈,栈帧中存放的都市一些变量名等东西,所以我们平常说栈中存放的是一些局部变量,因为局部变量就是在方法中。也就是在栈帧中,就是这样说过来的。
以上说的三个都是线程不共享的,也就是这部分内存,每个线程独有,不会让别的线程访问到,接下来的两个就是线程共享了,也就会出现线程安全问题。
4、堆
所有线程共享的一块内存区域。Java虚拟机所管理的内存中最大的一块,因为该内存区域的唯一目的就是存放对象实例。几乎所有的对象实例度在这里分配内存,也就是通常我们说的new对象,该对象就会在堆中开辟一块内存来存放对象中的一些信息,比如属性呀什么的。同时堆也是垃圾收集器管理的主要区域。因此很多时候被称为"GC堆",虚拟机的垃圾回收机制等下一篇文章来讲解。 在上一点讲的栈中存放的局部引用变量所指向的大多数度会在堆中存放。
5、方法区和其中的运行时常量池
和堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、和编译器编译后的代码(也就是存储字节码文件。.class)等数据,这里可以看到常量也会在方法区中,是因为方法区中有一个运行时常量池,为什么叫运行时常量池,因为在编译后期生成的是各种字面量(字面量的意思就是值,比如int i=3,这个3就是字面量的意思)和符号引用,这些是存放在一个叫做常量池(这个常量池是在字节码文件中)的地方,当类加载进入方法区时,就会把该常量池中的内容放入运行时常量池中。这里要注意,运行时常量池和常量池,不要搞混淆了,字节码文件中也有常量池,在后面的章节会详细讲解这个东西。现在只需要知道方法区中有一个运行时常量池,就是用来存放常量的。还有一点,运行时常量池不一定就一定要从字节码常量池中拿取常量,可能在程序运行期间将新的常量放入池中,比如String.intern()方法,这个方法的作用就是:先从方法区的运行时常量池中查找看是否有该值,如果有,则返回该值的引用,如果没有,那么就会将该值加入运行时常量池中。
二、练习。画内存图。
平常分析中用到的最多还是堆、虚拟机栈和方法区。
例如:看下面这段程序,然后画出内存分析图
最主要是看我的分析过程,这个图由于要显示出动态弹栈画不了,所以只能够那样画一下了。