图片很少,观众老爷们慎看,记录整理下来自己需要的时候回顾一下
JAVA体系架构图
oracle官网关于java结构的介绍链接https://docs.oracle.com/javase/8/docs/
1.jdk:java编译环境
2.jre:java运行环境
JVM是什么
Java Virtual Machine java虚拟机 简称JVM
我们都知道java是跨平台的,跨平台性就是通过JVM来实现的,
就是不同操作系统上面编写的.java文件通过javac编译成.class字节码码文件后jvm再去执行
这就类似于在不同机器上又跑了台编译java的机器
当然也有其他语言使用jvm编译,如Jruby、Groovy等
JVM内存结构
1.线程共享的
(1)方法区
可供各条线程共享的运行时内存区域,运行时常量池字段和方法数据、已被加载的类型信息、常量、静态变量、构成函数和普通方法的字节码内容等类结构信息存储在该区域
(java1.7的时候叫永久代,在java1.8的时候废除永久代概念,永久代只是用来实现方法区的)
1):运行时常量池是方法区的一部分,用来存储编译期间生成的各种字面量与符号引用
(2)堆
用来存放对象的实例(并不是所有的对象都会在堆上创建,经过逃逸分析【JVM优化的对象分配策略】的对象会在栈上分配)也被称为GC堆,因为垃圾回收的主要对象就是堆内存
1):堆的经典分代:可分为新生代(Eden、Survivor0【也叫作From】、Survivor1【也叫作To】)、老年代( 在G1收集器出现后堆内存被化整为零重新分配了,打破了经典分代设计)
新生的对象直接在Eden分配,没经过一次GC后,存活下来的对象会进入到Survivor0【也叫作From】区,第二次GC时,依旧存活的对象会分配到Survivor1【也叫作To】区,第三次CG存活又会复制到Survivor0【也叫作From】区,期间对象的年龄字节码+1,加到15次后被放入到老年代,这里复制类似于temp,需要一个缓存区域做复制交换,这也是为什么要分为From和to两个区域来存储活下来的对象(个人观点),两个区域内存比为8:1:1
老年代存放存活下来的对象还有大对象(占内存很大,在Eden区分配时超过特定的内存比例会直接放入老年代)
2):多线程下堆内存分配线程安全处理:采用cas配上失败重试的方式来保证更新操作的原子性
本地线程分配缓存(Thread Local Allocation Buffer TLAB)堆中划分的多个线 程私有的分配缓冲区
2.线程私有的
(1)虚拟机栈
虚拟机栈是每个线程所私有的,线程运行时里面的方法就会打包成栈帧放入虚拟机栈,方法的调用执行顺序就是栈帧在虚拟机栈中入栈出栈的过程
1)栈帧 ,栈帧又分为以下几个区域
局部变量表:存放方法中的局部变量,主要是八大基本数据类型,如果是对象则放入对象的引用
操作数栈:方法内的计算和逻辑处理,都是在操作数栈中来进行的,比如一个方法里需要进行计算,这是就需要在操作数栈中通过字节码的压栈和弹出来进行计算,这里类似于CPU的高速缓冲,CPU只计算不存储,所以这里通过操作数栈压入数据通过字节码运算然后弹出计算后的值
动态链接:部分符号引用在运行期间转化为直接引用这种转化称为动态链接,比如java的多态、java反射、lambda表达式(底层是调用invokedynamic字节码通过方法句柄(MethodHandle)实现,方法句柄是一个能够执行的引用)
完成出口:方法的返回
(2)本地方法栈
类似于虚拟机栈,但是服务对象是native方法,native方式是C语音实现的与操作系统底层交互的代码
(3)程序计数器
虚拟机栈中还存放有程序计数器,记录各个线程执行的字节码地址,程序技术器是jvm唯一不抛出OutOfMemory的内存区域
为什么需要程序计数器
1)java是可以对线程编程的这里用来记录,当前线程运行时,如果CPU资源被其他线程占用了,则就需要程序计数器来记录当前线程的代码执行行数。
2)jvm虚拟机就相当于跑在操作系统上的一台机器,操作系统底层有程序计数器,jvm也有,java是多线程语言,当运行线程数操作CPU核数时,线程之间需要根据时间片轮训机制来争夺CPU资源,如果当前java线程的CPU资源被系统其它线程占用,这时也需要程序计数器来记录下一条运行的指令
下面就是有关的GC知识点
大家都知道java是自动管理内存的,对于不使用的对象就通过GC回收掉内存
对象创建过程及内存分配
1.对象分配
2.多线程情况保证安全: