注:本文是参考了深入理解java虚拟机 JVM高级特性与最佳实践 2.2这一部分
java运行时数据区域有以下几个部分:
1,程序计数器(线程私有)
作用:看做是当前线程所执行的字节码的行号指示器,字节码解释器工作时通过改变计数器的值来找到要执行的下一条字节码指令,
分支、循环、跳转、异常等等都需要依赖这个计数器。
Java虚拟机多线程是通过线程轮流切换并分配处理器的执行时间的方式来实现。所以从这里看出这个程序计数器是每个线程一个。(线程单例)
当线程执行得是一个Java方法计数器记录着正在执行的这个字节码指令的地址,当执行得是Native方法时,这个计数器的值为空(Undefined),
这个内存区域是唯一一个在JVM中没有规定任何OutOfMemoryError的区域。
2,java虚拟机栈(线程私有)
生命周期与线程相同。
每个方法执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。(动态链接、方法出口又称为栈帧信息)
每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
这也就是我们一直说(程序员常讨论的栈就是这个)的栈可能只包括虚拟机栈的局部变量表。
a,局部表量表:
局部变量表存放了编译期可知的各种基本数据类型、对象引用、returnAddress类型。
而returnAddress是为字节码指令jsr、jsr_w和wet服务的,它指向了一条字节码指令的地址,以前的虚拟机使用它来实现异常处理,现在用异
常表(知道有这个即可)
局部变量的容量以变量槽(Slot),虚拟机规范中并没有明确的指明一个Slot占的内存多大,只是说每个Slot都能存放一个boolean、byte、char、
short、int、float、对象引用(reference)、returnAddress。
long、double是64位的,占两个Slot,当读写连续的两个Slot时具有原子性(即不可分)虚拟机不允许采用任何方式单独访问一个。
b,操作数栈
也常称为操作栈:方法执行过程中会有各种字节码指令通过操作数栈进行,
调用其他方法时也是通过操作数栈来进行参数传递。
比如:两个int型的数据进行加法运算时就是在操作数栈;
两个方法的操作数栈与局部变量表可能会重合一部分方便了参数的传递;
3,本地方法栈Native Method Stack(线程私有)
与虚拟机栈发挥的作用相同,区别只是虚拟机栈为虚拟机栈执行Java方法服务,本地方法则为虚拟机使用到的Native方法服务(有的虚
拟机会直接把两个栈合为一个),本地方法栈也会抛出,StackOverflowError和OutOfMemoryError异常。
Native方法
Native方法?native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)
实现的文件中。Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。
JNI 就是 java Native Interface 的缩写, 是 Java 语言与其他类型语言交互的接口. 当出现一些用java语言无法处理的任务时,开发人
员就需要利用JNI技术来完成。
4,方法区(线程共享)
用于存储已被虚拟机加载的类信息、常量、静态变量等
java规范把方法区描述为堆得一个逻辑部分。
别名永久代。
5,堆(线程共享)
Java虚拟机管理的内存中最大的一块。
虚拟机启动时创建;
存在的意义就是存放对象实例;
堆也是垃圾收集器里的主要区域
问题?对象什么可以回收?
没有引用的时候
什么时候确定对象可以回收?
6,引用计数器
给对象添加一个引用计数器,每当有一个地方引用他时,计数器值就加1;当引用失效时计数器就减1;
实现简单,效率较高,也有很多语言在用比如Python语言。
问题?
如:A对象有一个属性是B,B对象有一个属性是A他两互相引用但都不被其他地方引用,因为他们的引用计数器都不为零这样就无法回收。
所以主流的java虚拟机没有采用这个算法来管理内存。
7,可达性分析算法
主流的商用程序语言(Java\C#)都是通过可行性分析来判定对象是否存活的。这个算法基本思想就是通过称为“GC Roots”的对象作为起始点
从这些节点开始向下搜索,当一个对象不可达的时候就说明对象是不可用的了。会被回收。
可以简单的理解从虚拟机栈中的引用去找,不可达的就是可以回收的。