龙空技术网

Java互联网架构-深入浅出JVM虚拟机

图灵学院官方 218

前言:

今天你们对“java互联网架构是什么”大概比较重视,兄弟们都需要学习一些“java互联网架构是什么”的相关内容。那么小编同时在网络上网罗了一些关于“java互联网架构是什么””的相关文章,希望姐妹们能喜欢,姐妹们一起来了解一下吧!

欢迎关注头条号:java小马哥

周一至周日早九点半!下午三点半!精品技术文章准时送上!!!

精品学习资料获取通道,参见文末

一、JVM运行时数据区域

1.1、程序计数器

一块较小的内存空间,当前线程所执行的字节码指示器。每个线程有一个独立的程序计数器

1.2、Java虚拟机栈

线程私有,生命周期与线程相同

每个方法在执行时会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息

64位的long和double类型的数据会占用两个局部变量空间,其余数据类型占用1个

局部变量表存放了编译期间可知的各种基本数据类型,对象的引用

异常:1)线程请求的深度大于虚拟机所允许的深度,抛出StackOverflowError

2)如果虚拟机可以动态扩展,扩展时无法申请到足够的内存,抛出OutOfMemoryError

1.3、本地方法栈

与虚拟机栈作用相似,区别就是虚拟机栈执行java方法(字节码服务),方法栈执行的时Native方法服务,也会抛出StackOverflowError和OutOfMemoryError

1.4、java堆

JVM管理最大的一块内存,所有线程共享的一块内存区域,存放实例对象及数组

垃圾收集齐管理的主要区域

,扩展通过-Xmx -Xms控制

1.5、方法区

加载的类信息,常量,静态变量,各线程共享区域

1.6、运行时常量池

方法区的一部分

1.7、直接内存

二、对象的创建【new-->加载类-->分配空间-->设置对象-->init】

1)收到new指令----根据参数检测常量池中定位到类的引用------检查是否被加载,解析,初始化------没有就优先执行类加载

2)类加载完毕----分配内存【1.指针碰撞[空内存与使用内存规则,垃圾收集器需要带压缩整理功能(Serial,ParNew)]2.空闲列表[不规则(CMS)]】

3)内存分配完---内存空间都设置为0,使用TLAB可将此步骤提前到TLAB分配时进行

4)设置对象

5)new 完成之后执行init方法,这样一个真正可用的对象就算生产出来了

6)修改指针的线程安全性

对分配内存的动作进行同步处理

每个线程在java堆中预先分配一小块内存,本地线程分配缓存(TLAB),哪个线程需要分配内存,就在哪个线程的TLAB上分配,只有TLAB用完重新分配时才加同步锁定

,通过参数 -XX:+/-UseTLAB参数设定

三、对象的布局

3.1.分为3块区域 对象头(Header) 实例数据(Instance Data) 对齐填充(Padding)

3.2.Header:对象自身运行时数据,哈希码,GC分代年龄,锁状态标识等

3.3.实例数据部分:对象真正存储的有效信息

3.4.对齐填充:不是必然存在的,占位符的作用,对象的大小必须是8字节的整数倍

四、对象的访问定位

4.1.通过栈上的reference数据来操作堆上的具体对象,访问方式:句柄访问和直接指针两种

4.2.句柄访问:java堆中将会划分出一块内存来做句柄池,reference中存储的就是对象的句柄地址,句柄中包含了对象实例数据与类型数据各自的具体地址信息

4.3.直接指针:reference中存储的直接就是对象地址

4.4.句柄最大好处就是reference中存储的时稳定的句柄地址,在对象移动时只会改变句柄中的实力数据指针,reference本身不需要修改

4.5.直接指针:速度快,节省了一次指针定位的时间开销

4.6.引用:如果reference类型的数据中存储的数值是另外一块内存的起始位置就称这块内存代表这一个引用

4.7.判断对象是否存活

引用计数器算法 可达性分析算法

4.8.可做Roots对象

虚拟机栈中引用的对象 方法区中静态属性引用 方法区中常量引用的对象 本地方法栈中引用的对象

4.9.引用分类

强引用 弱引用 虚引用 软引用

4.9.1.强引用:程序代码中普遍存在的,Object obj = new Object();只要强引用还存在,垃圾回收器永远不会回收掉被引用的对象

4.9.2.软引用:一些还有用但并非必须的对象,在系统发生内存溢出异常之前,会把这些对象列进回收范围之内进行二次回收,回收之后还没有足够的内存才会抛出

内存溢出,JDK1.2提供了SoftReference实现软引用

4.9.3.弱引用:非必须对象,强度比软引用更弱一些,只能生存到下一次垃圾手机发生之前,WeakReference实现弱引用

4.9.4.虚引用:幽灵引用或幻影引用,最弱的引用关系,PhanttomReference

4.9.5.可达性分析算法计算不可达也不会真正认为对象死亡,需要再经果两次标记 1)标记是否需要执行finalize()方法 2)有必要执行放入F-Queue队列,GC会对里

面的对象进行二次标记

五、垃圾收集算法

5.1.标记-清除算法

两个阶段:1)标记所有需要回收的对象 2)标记完成后统一回收所有被标记的对象

缺点:效率不高,两个阶段都不高 会产生大量不连续的内存碎片,分配较大对象的时候会由于找不到连续的较大内存不得不触发另一次垃圾回收动作

5.2.复制算法

将内存按容量大小分配为相等的两块,每次只使用其中的一块,当一块用完之后将还存活的对象复制到另外一块上去,然后再把使用过的内存空间清理掉,简单高效

,代价就是将内存缩小为原来的一半

5.3.分代收集算法

Java堆分成新生代和老生代,新生代采用复制算法,老生代采用标记-清除的算法

六、垃圾收集器

Serial收集器

最基本.发展历史最悠久的收集器

单线程收集齐

工作时必须暂停其他所有的工作线程,直到收集结束

parNew收集齐

Serial收集器的多线程版本

出了Serial收集齐外,唯一可以与CMS一起使用的收集器

-XX:+UseConcMarkSweepGC默认使用

-XX:+UseParNewGC强制指定

默认开启的线程和CPU数量相同,可使用-XX:ParallelGCThreads参数限制垃圾收集器线程数量

parallel scavenge收集器

新生代收集器,并行多线程收集器

关注的是吞吐量 运行用户代码时间/(运行用户代码时间+垃圾收集器时间)

-XX:MaxGCPauseMillis最大垃圾收集停顿时间 -XX:GCTimeRatio 吞吐量大小

Serial Old收集器

老年代收集器,单线程,使用标记-整理算法

parallel old

老年代收集器,多线程

cms收集器

以获取最短回收停顿时间为目标的收集器

标记-清除算法

分为4步骤 初始标记 并发标记 重新标记 并发清除

JDK1.6 默认触发阈值为92% 可使用-XX:CMSInitiatingOccupancyFraction

G1收集器

并行与并发

分代收集

空间整合

可预测的停顿

七、内存分配与回收策略

7.1.内存管理可以归结为自动化解决两个问题:给对象分配内存以及回收分配给对象的内存

7.2.对象优先再Eden分配

大多数情况下,对象在新生代Eden区中分配,当Eden不足分配时,虚拟机将发起一次Minor GC

-XX:+PrintGCDetails打印回收日志

7.3.大对象直接进入老年代

需要连续内存空间的java对象,长字符串以及数组

-XX:PretenureSizeThreshold设置值的对象直接在老年代分配

7.4.长期存活的对象将进入老年代

对象年龄,在新生代经果一次Minor GC后仍然存活,并能被Survivor容纳的话就被移到Survivor空间中,并且对象年龄为1,在Survivor区中每经过一次Monir GC

年龄+1,默认为15岁晋升到老年代

通过-XX:MaxTenuringThreshold设置阈值

7.5.动态对象年龄判断

虚拟机并不是永远要求对象的年龄必须到达了阈值才会进入老年代,如果Survivor空间中相同年龄所有对象大于Survivor空间的一半,年龄大于或等于改年龄的对象

直接进入老年代

八、虚拟机性能监控与故障处理工具

8.1.jps 显示指定系统内的HotSpot虚拟机进程

jstat 用于收集HotSpot虚拟机各方面的运行数据

jinfo 显示虚拟机配置信息

jmap 生成虚拟机的内存转储快照

jhat 用于分析heapdump文件,它会建立一个HTTP/HTML服务器,让用户可以在浏览器上分析结果

jstack 显示虚拟机的线程快照

九、内存间交互操作

9.1.lock:锁定,作用于主内存的变量,把变量标识为一条线程独占的状态

unlock:解锁,作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放之后的变量才可以被其它线程锁定

read:读取,作用于主内存的变量,它把一个变量从主内存从传输到线程的工作内存中,以便随后的load动作使用

load:载入,作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中

use:使用,作用于工作内存,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作

assign:赋值,作用于工作内存,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作

store:存储,作用于工作内存,它把工作内存中的一个变量的值传送到主内存中,以便随后的write操作

write:写入,作用于主内存,它把store操作从工作内存中得到的变量值放入到主内存的变量中

9.2.变量内存交互限制

1)不允许read和load,store和write单独出现

2)不允许一个线程丢弃它最近的assign操作,即变量在工作内存改变了之后必须写回主内存

3)不允许一个线程没有assign操作,就把变量同步回主内存

4)变量的诞生必须在主内存,不允许在工作内存使用一个未被初始化(load和assign)的变量,在use.store之前必须执行load和assign操作

5)一个变量在同一时刻只允许一条线程对其进行lock操作操作,但lock操作可以被同一条线程多次执行

6)如果对一个变量执行lock之后,那将会清空工作内存此变量的只,在执行引擎使用这个变量前,需要重新执行load和assign操作

7)如果一个变量事先没有执行后lock操作,不允许执行unlock操作

8)对一个变量执行unlock之前,必须先把此变量同步回主内存中

9.3.volatile 最轻量级的同步机制

保证此变量的可见性

volatile变量一条线程修改变量值,新值对于其它线程来说是可以立即得知的

普通变量需要通过主内存的来完成,线程A修改了变量age,需要先回写回主内存,线程B等线程A回写完成之后再从主内存进行读取操作

禁止指令重排序优化

9.4.先行发生原则

判断数据是否存在竞争,线程是否安全的主要依据

1)如果线程A先行发生于操作B,操作A产生的影响能被操作B观察到

2)程序次序规则:在一个线程内,按照程序代码顺序,书写在前面的操作先行发生于写在后面的操作

3)管程锁定规则:一个unlock操作先行发生于后面对同锁的lock操作【时间先后】

4)volatile变量规则:对一个volatile变量的写操错先行发生于后面这个变量的读操作【时间先后】

5)线程启动规则:Thread对象的start()方法先行发生于此线程的每个动作

6)线程终止规则:线程中的所有操作都先行发生于对此线程的终止检测

7)线程中断规则:对线程interrup()方法的调用先行发生于被中断的代码检测到中断事件的发生

8)对象终结规则:一个对象的初始化完成先行发生于finalize()方法的开始

9)传递性:A>B B>C 则 A>C

十.线程的实现

使用内核线程实现,使用用户线程实现,用户线程加轻量级进程

封面图源网络,侵权删除)

私信头条号,发送:“资料”,获取更多“秘制” 精品学习资料

如有收获,请帮忙转发,您的鼓励是作者最大的动力,谢谢!

一大波微服务、分布式、高并发、高可用的原创系列文章正在路上,

欢迎关注头条号:java小马哥

周一至周日早九点半!下午三点半!精品技术文章准时送上!!!

十余年BAT架构经验倾囊相授

标签: #java互联网架构是什么 #深入浅出java虚拟机设计与实现 pdf