1.Java虚拟机本质与编译器由来
Java虚拟机jvm(一个c/c++程序而已,当然也有其他语法实现的)其实是一个进程虚拟机--上面跑进程的(,普通服务器虚拟机是跑操作系统的),启动一个java程序就是启动一个java虚拟机上面跑这个java程序,这个启动的java虚拟机对操作系统来说其实就是一个(c++)进程,这个进程会调用本地方法(java中叫本地方法,其实就是C/C++的库函数,C/C++汇编语言编写,编译成和处理器相关的机器代码,存储在动态链接库中)
Java虚拟机怎么跑java程序的呢?类装载器装载java程序编译后的字节码(class文件-与平台无关)和Java API(只有程序执行时需要的class才会被装载)--》执行引擎即时翻译成本地方法调用,调用操作系统资源。运行中Java程序的每一个线程都是一个独立的虚拟机执行引擎的实例。从线程生命周期的开始到结束,它要么在执行字节码,要么执行本地方法。
最开始第一个编译器怎么来的?最开始人对照机器指令写的程序,编译器也是程序,所以是人直接用二进制写的,就有了汇编,汇编写了B语言编译器-》C语言……机器码生汇编,汇编生B,B生C,C生万物
2.JVM内存模型
1)内存区域划分

java虚拟机规范中把所管理的内存区域-运行时数据区包括方法区、虚拟机栈、本地方法栈、堆、和程序计数器,前面说的jvm就是个C/C++程序,
C/C++程序能自己管理控制生命周期和垃圾回收是C/C++程序内存四区中的堆区。所以个人认为运行时数据区中方法区、堆区是在C/C++程序内存四区中的堆区。jvm线程栈和本地方法栈映射到了C/C++程序的栈区
- 方法区:
- 虚拟机栈:
- 本地方法栈:
- java堆:
- 程序计数器:
- 本地直接内存:不属于jvm运行时数据区,飘离于java堆,个人理解认为一般的堆对象要通过jvm做一次包装加工(如对象头、地址定位转换)由jvm去操作那段内存,管理回收那段内存,直接内存是中间少包装转换,也不受jvm常规垃圾收集器处理。直接内存一般有两种使用方式:
- Unsafe:allocateMemory方法申请内存和freeMemory方法用来自己释放内存
- DirectBuffer:是对Unsafe的封装,内存释放也帮我们做了 ,不需要我们手动释放内存,在DirectBuffer对象被垃圾回收时会触发直接内存的释放(java的Reference与DirectBuffer的内存回收、DirectBuff释放内存)
2)对象内存布局与访问定位
- HotSpot虚拟机的对象头包括两部分信息,第一部分为对象自身的运行时数据,如hashCode、Gc分代年龄、锁状态标志、线程持有的锁、偏向线程id等;另外一部分是类型指针,指向类元数据的指针(并不是所有虚拟机都这么做的)。如果对象是数组那么对象头中还得有数组长度
- 实例数据:类中定义的字段,无论从父类继承的还是子类定义的,各个字段的存储顺序和虚拟机分配策略有关以及java源码变量定义顺序有关。
- 对齐填充:8字节对齐
- 句柄访问:需要在java堆中划一段内存来作为句柄池,引用存的就是对象的句柄地址,而句柄包括对象实例数据与类类型数据的具体地址信息

- 直接指针访问:那么引用存的就是实例对象的地址,而类类型数据就得在实例对象中实现获取了。

3).变量存储内存区域总结
- 一个本地变量可能是原始类型,在这种情况下,它总是“呆在”线程栈上。
- 一个本地变量也可能是指向一个对象的一个引用。在这种情况下,引用(这个本地变量)存放在线程栈上,但是对象本身存放在堆上。
- 一个对象可能包含方法,这些方法可能包含本地变量。这些本地变量任然存放在线程栈上,即使这些方法所属的对象存放在堆上。
- 一个对象的成员变量随着这个对象自身存放在堆上。不管这个成员变量是原始类型还是引用类型。
- 静态成员变量(引用)跟随着类定义一起也存放在方法区上。如果静态成员变量是引用类型new出来的,那么new出来的实际对象存储在堆上,只是这个静态引用是在方法区,这个跟本地变量有点相似。所以在类中声明一个静态成员变量并用new赋初值是完全可以的。单例模式就可以这么实现而不需要用双重检验锁。Java语言并不要求常量一定只有编译期预置入Class的常量表的内容才能进入方法区常量池,运行期间也可将新内容放入常量池(最典型的String.intern()方法)
- 存放在堆上的对象可以被所有持有对这个对象引用的线程访问。当一个线程可以访问一个对象时,它也可以访问这个对象的成员变量。如果两个线程同时调用同一个对象上的同一个方法,它们将会都访问这个对象的成员变量,但是每一个线程都拥有这个本地变量的私有拷贝。