一 , 前言
JVM 是什么, 我想诸位肯定都清楚.
好吧, 我还是简答说一下 JVM 即 Java 虚拟机 (够简单吧 233333).
虽然说, 所有抛开操作系统, 讲虚拟机的内容, 都是耍流氓. 但是, 贫僧不修善果, 就爱杀人放火, 就爱耍流氓. 好吧, 扯远了.
言归正传, JVM 这是第三遍重温了. 第一遍读时, 还是在飞机上读的, 就记得飞机上的阳光很刺眼, 肚子很难受, 从书中汲取的知识很少. 第二遍读时, 是在做地铁时看完的. 地铁很挤, 书很好看. 如今, 距离上次读完, 已有一载有余, 一年发生了很多事, 朋友离开, 公司散伙, 亲人重病. 期间明白了很多道理. 一个是, 一定要好好的关爱你的亲人. 一个是, 对于人重要的东西只有两样, 健康的身体, 以及独立的灵魂. 最近读完了史铁生的《我与地坛》对这两点也是感触颇多.
又扯远了. 下面进入正题.
二, 内存划分
总体划分如下:
三, 运行时数据区域
运行时数据区域, 我们可以分为线程私有的数据区域, 与线程共享的数据区域. 线程私有, 也就是线程内的数据, 是能且仅能让创建它的线程访问. 线程共享, 是任何线程都可以访问共享的此数据.
1, 线程私有
(1) 程序计数器
程序计数器可以视为当前线程执行的字节码行数指示器.(程序计数器是什么)
多线程运行时, 本质上, 是线程的轮流切换. 这一点, 如果对操作系统还有记忆的同学, 可能感觉如此之熟悉, 是的单核的操作系统中, CPU 在执行多任务时, 就是将每个任务都执行一点, 宏观意义上来讲, 就达到了多任务同时进行的感觉. 而当虚拟机中多线程执行时, 线程执行了一半, 就执行其他线程了, 又一次回到此线程时, 如何确保可以继续执行线程, 这里, 就是程序计数器的意义所在了.(为什么要有程序计数器)
一个线程有且仅有一个程序计数器.(有第一点和第二点而得出的结论)
(2)java 虚拟机栈
[1] 栈帧
在每个方法被创建时, 同时也会创建一个栈帧.
一个方法被调用到被执行完毕的过程, 就是一个栈帧, 在虚拟机栈中, 从入栈到出栈的过程.
[2] 栈帧的数据结构
一个栈帧中包括了局部变量表, 操作数栈, 动态链接, 以及方法出口
这里我们详细的来说一下他的局部变量表
1) 存放了基础数据类型.
2) 存放了对象的引用
3) 存放了 returnAddress: 指向一条直字节码令的地址
[3] 基础数据类型扩展
重温此结的时候, 突然想到了基础数据的一个共同点.
他们都可以被转为为 int
- public static void main(String[] args) {
- int i = 1;
- short s = 1;
- float f = 1f;
- double d = 1;
- long l = 1L;
- char c = 63;
- byte b2 = 1;
- boolean b = 1;
- System.out.println("输出 char>>"+c);
- }
以上代码在编译期间, 最后一行, 也就是 boolean 会报错, 但是学过 c 或 c++ 的都知道, bool 类型, 本质上, true 为 1,false 为 0.
而其他的都是可以正常赋值的.
char 的赋值, 大家可以猜猜, 输出的是多少. 是 63 的 ASCII 值 "?"
输出 char>>?
(3) 本地方法栈
本地方法栈, 他与虚拟机栈类似 (数据结构以及功能等方面)
不同的是, 虚拟机栈执行的是 java 方法 (编译出的字节码). 而本地方法栈执行的是 Native 方法. 比如:
Thread 类中的
- private native void start0();
- // 调用 dll 或其他文件内方法
- public native static void Hello();
有些虚拟机, 他的本地方法栈与虚拟机栈会合并.
2, 线程共享
(1)java 堆
堆内的数据, 是所有线程共享的.
几乎所有的对象实例都在此存储, 因此, java 堆又被称为 GC 堆
(2) 方法区
[1] 方法区的数据结构
类信息: 包括了类的版本, 类中的字段, 方法, 接口以及常量池. 常量池在编译期就会确认并生成.
常量.
静态常量.
编译后的代码.
[2] 逻辑上是堆的一部分, 但他的别名是非堆.
2, 非运行时数据区域
(1) 直接内存
NIO 使用此块内存, 以提高读写性能
四, 参考
《深入理解 java 虚拟机》
来源: https://www.cnblogs.com/xuyuanpeng/p/11098583.html