JV学习笔记

第一章、简介
Java虚拟机的主要内容包括:
1、虚拟机的内部结构;
2、虚拟机执行的字节码类型和功能;
3、Class文件的结构;
4、类的装载、连接和初始化

第二章、虚拟机的结构
虚拟机的基本结构包括:类加载系统、方法区、堆、java栈、本地方法栈、PC寄存器、直接内存、垃圾回收系统、执行引擎。
java进程使用方法:java [-options] class [args…]
options表示启动参数;
args表示传递给主函数main()的参数

java堆分为新时代和老年代。
老年代 : 三分之二的堆空间
年轻代 : 三分之一的堆空间
新时代又分为eden区、s0区、s1区;s0和s1是两块大小相等、可以互换的内存空间。
eden区: 8/10 的年轻代空间
survivor0 : 1/10 的年轻代空间
survivor1 : 1/10 的年轻代空间

大多数情况下,对象首先分配到eden区,经过一次新生代回收后,如果对象还存活,就会进入到s0或者s1,每经过一次新生代回收,对象存活,年龄就加1,当年龄达到一定数量,就会被认为是老年对象,进入老年代。

java栈保存的内容是栈帧,每一次函数调用就压入一个栈帧,函数结束就弹出一个栈帧。
一个栈帧中至少要包含局部变量表、操作数栈和帧数据。
操作数栈用来保存计算过程的中间结果。

第三章、参数设置
-XX:+PrintFlagsFinal 查看所有默认的jvm参数
-Xss 128k设置栈空间
-XX:+PrintGC 打印垃圾回收日志
-Xmx 10m 最大堆内存
-Xms 10m 最小堆内存
-XX:+PrintGCDetails 打印日志详情
-XX:InitialSurvivorRatio=8 新生代Eden/Survivor空间的初始比例
-XX:Newratio=2 老年代区 和 新生代区的内存比例
-XX:MetaspaceSize=512m 初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
-XX:MaxMetaspaceSize=512m 最大空间,默认是没有限制的
-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集
-XX:+PrintHeapAtGC 在GC前后输出堆日志
-XX:+PrintGCTimeStamps 在GC发生时输出GC发生时间
-XX:+TraceClassLoading 跟踪类的加载
-XX:+PringVMOptions 打印命令行接受的显式参数
-XX:+PringCommandLineFlags 打印命令行接受的显式和隐式参数
实际中可以将-Xmx和-Xms设置相等。
-Xmn 10m 设置新生代的内存
-XX:+HeapDumpOnOutOfMemoryError 内存溢出时导出错误信息
-XX:HeapDumpPath=d:/a.dump 存放路径
-XX:MaxDirectMemorySize 最大直接内存,如果不设置就默认最大堆空间,直接内存到达设置的值时会触发垃圾回收
-Xloggc:/path/gc.log 垃圾回收日志

-XX:+UseSerialGC 指定使用新生代串行收集器和老年代使用串行收集器
-XX:+UseParNewGC 新生代使用ParNew回收器和老年代使用串行收集器
-XX:+UseParallelGC 新生代使用ParallelGC回收器和老年代使用串行收集器
-XX:+UseParallelOldGC 新生代使用ParallelGC回收器和老年代使用ParallelOldGC收集器
-XX:+UseConcMarkSweepGC 新生代使用ParNew回收器和老年代使用CMS回收器
-XX:+UseG1GC 使用G1收集器
-XX:MaxTenuringThreshold=15 新生代晋升老年代的次数
-XX:ParallelGCThreads 垃圾回收的线程数

第四章、垃圾回收概念及算法
引用计数法、标记压缩算法、标记清除算法、复制算法和分代、分区思想。
标记清除算法最大的缺点是空间碎片
复制算法将内存分为2块,每次只使用其中的一块。需要复制存活的对象很少。缺点是内存需要分成2块来使用。
标记压缩算法是一种老年代算法,去除了标记清除算法的空间碎片的缺陷。
分代算法将内存根据对象的特点分成几块,使用不同的回收算法,提高回收的效率。
分区算法将堆空间分为连续的不同小空间,每个小空间都独立使用,独立回收,可以控制一次回收多少个小空间。

垃圾对象的判断标准是不可触及。
可触及包括3种状态:1、可触及性:从根节点开始,可以到达对象;
2.可复活的:对象的引用被释放,但对象可能在finalize()中复活;
3、不可触及的:对象的finalize()已经被调用,并且没有复活,进入到不可触及状态。
对象不可触及时才可以被回收。

四种级别的引用:强引用、软引用、弱引用和虚引用。
强引用对象是可触及,不会回收的。软引用、弱引用和虚引用在一定条件下是可以被回收的。

软引用在堆空间不足的时候会被回收;
弱引用只要被发现,就会被回收;
虚引用随时都可能被回收,必须和引用队列一起使用,主要用于跟踪垃圾回收过程。
垃圾回收的过程中,应用程序会出现停顿,不会有响应,也称为STW。

第五章、垃圾收集器及内存分配
垃圾收集器种类:串行垃圾收集器、并行垃圾收集器、CMS回收器、G1回收器。
串行回收器指单线程进行垃圾回收的回收器。特点是单线程、独占式。垃圾回收时,线程都需要暂停。
新生代串行垃圾回收器使用复制算法。
老年代串行垃圾回收器使用的是标记压缩算法。

并行垃圾回收器在串行的基础上改进,使用多个线程同时进行垃圾回收。
ParNew回收器:用于新生代,使用多线程进行回收,并行垃圾回收器。
ParallelGC回收器:用于新生代,使用多线程进行回收,并行垃圾回收器。关注系统吞吐量
ParallelOldGC回收器:用于老年代,使用多线程进行回收,并行垃圾回收器,关注系统吞吐量。

CMS回收器:多线程并行回收器,关注系统停顿时间。步骤是:初始标记(标记根对象)、并发标记(标记所有对象)、预清理、重新标记、并发清除、并发重置。初始标记和重新标记是独占系统资源,其他的可以和用户线程一起执行。

G1回收器:属于分代回收器,使用了分区算法,特点并发性、并行性、分代GC、空间整理、可预见性。
收集过程有4个阶段:新生代GC、并发标记周期、混合收集、可能继续FullGC。
如果对象很大,新生代容纳不下,直接放到老年代。

第十章、Class加载系统
系统加载Class文件可以分为加载、连接和初始化。其中连接又分为验证、准备和解析。
Class只有在必须使用的时候才会加载;虚拟机规定,一个类或接口必须在使用前初始化,这里的使用指主动使用,主动使用有下列几种情况:
1、创建一个类的实例;
2、调用类的静态方法;
3、使用类或接口的静态字段(final修饰除外);
4、main()方法的类

加载类时,虚拟机完成以下 工作:1、通过类的全名,获取类的二进制流;2、解析类的二进制流为方法区内的数据结构;3、创建Class类的实例
验证类:格式检查、语义检查、字节码验证和符号引用验证。
准备:为类分配内存空间,并设置初始值。
解析:将类、接口、字段和方法的符号引用转为直接引用。
初始化:执行类的初始化方法

Java虚拟机会创建3类CLassLoader,分别是BootStrap ClassLoader、Extension ClassLoader、App ClassLoader。
判断类是否加载时,应用类加载器会顺着双亲路径往上判断,直到启动类加载器。缺点是顶层的类加载器无法访问底层的类加载器所加载的类。

第八章、锁和并发
锁的作用保护临界区的资源不会被多个线程同时访问而受到破坏。
对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)
对象头(Object Header)包括两部分信息,第一部分用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,这部分数据的长度在32位和64位的虚拟机(暂 不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为“Mark Word”。
对象头的另外一部分是类型指针,即是对象指向它的类的元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。并不是所有的虚拟机实现都必须在对象数据上保留类型指针,换句话说查找对象的元数据信息并不一定要经过对象本身。另外,如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中无法确定数组的大小。
锁的状态总共有四种:无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁(但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级)。

实例数据:真正存储有效信息,即程序代码中所定义的各种类型的字段内容,包括父类继承下来的和子类定义的。
无锁 (001)
偏向锁 (101)
轻量级锁 (00)
重量级锁 (10)
对象头由以下三部分组成:Mark Word、指向类的指针、数组长度(只有数组对象才有)
JVM一般是这样使用锁和Mark Word的:1,当没有被当成锁时,这就是一个普通的对象,Mark Word记录对象的HashCode,锁标志位是01,是否偏向锁那一位是0。

2,当对象被当做同步锁并有一个线程A抢到了锁时,锁标志位还是01,但是否偏向锁那一位改成1,前23bit记录抢到锁的线程id,表示进入偏向锁状态。

3,当线程A再次试图来获得锁时,JVM发现同步锁对象的标志位是01,是否偏向锁是1,也就是偏向状态,Mark Word中记录的线程id就是线程A自己的id,表示线程A已经获得了这个偏向锁,可以执行同步锁的代码。

4,当线程B试图获得这个锁时,JVM发现同步锁处于偏向状态,但是Mark Word中的线程id记录的不是B,那么线程B会先用CAS操作试图获得锁,这里的获得锁操作是有可能成功的,因为线程A一般不会自动释放偏向锁。如果抢锁成功,就把Mark Word里的线程id改为线程B的id,代表线程B获得了这个偏向锁,可以执行同步锁代码。如果抢锁失败,则继续执行步骤5。

5,偏向锁状态抢锁失败,代表当前锁有一定的竞争,偏向锁将升级为轻量级锁。JVM会在当前线程的线程栈中开辟一块单独的空间,里面保存指向对象锁Mark Word的指针,同时在对象锁Mark Word中保存指向这片空间的指针。上述两个保存操作都是CAS操作,如果保存成功,代表线程抢到了同步锁,就把Mark Word中的锁标志位改成00,可以执行同步锁代码。如果保存失败,表示抢锁失败,竞争太激烈,继续执行步骤6。

6,轻量级锁抢锁失败,JVM会使用自旋锁,自旋锁不是一个锁状态,只是代表不断的重试,尝试抢锁。从JDK1.7开始,自旋锁默认启用,自旋次数由JVM决定。如果抢锁成功则执行同步锁代码,如果失败则继续执行步骤7。

7,自旋锁重试之后如果抢锁依然失败,同步锁会升级至重量级锁,锁标志位改为10。在这个状态下,未抢到锁的线程都会被阻塞。

对齐填充:JVM要求java的对象占的内存大小应该是8bit的倍数,所以后面有几个字节用于把对象的大小补齐至8bit的倍数,没有特别的功能。

32位系统,对象的对象头如下:25bit表示对象的哈希值,4bit表示对象的年龄,1bit表示偏向锁,2bit表示锁信息。
在这里插入图片描述
上图显示的是不同情况下对象头的格式。

应用层面的锁优化思路:1、减少锁持有时间;2、减少锁的粒度;3、锁分离

CAS:比较并替换算法;包含3个参数,要 更新的变量,预期值,新值

Java内存模型是围绕着并发编程中原子性、可见性、有序性这三个特征来建立的。
Java内存模型的主要目标是定义程序中各个变量的访问规则,即在JVM中将变量存储到内存和从内存中取出变量这样的底层细节。
JMM规定了所有的变量都存储在主内存(Main Memory)中。每个线程还有自己的工作内存(Working Memory),线程的工作内存中保存了该线程使用到的变量的主内存的副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量(volatile变量仍然有工作内存的拷贝,但是由于它特殊的操作顺序性规定,所以看起来如同直接在主内存中读写访问一般)。不同的线程之间也无法直接访问对方工作内存中的变量,线程之间值的传递都需要通过主内存来完成。

Java内存模型中定义的两项操作之间的次序关系,如果说操作A先行发生于操作B,操作A产生的影响能被操作B观察到。
happens-before原则:
1、程序次序规则(Pragram Order Rule):在一个线程内,按照程序代码顺序,书写在前面的操作先行发生于书写在后面的操作。
2、volatile变量规则(Volatile Variable Rule):对一个volatile变量的写操作先行发生于后面对这个变量的读取操作
3、锁定规则(Monitor Lock Rule):一个unlock操作先行发生于后面对同一个锁的lock操作。这里必须强调的是同一个锁,而”后面“是指时间上的先后顺序。
4、传递性(Transitivity):如果操作A先行发生于操作B,操作B先行发生于操作C,那就可以得出操作A先行发生于操作C的结论。
5、线程启动规则(Thread Start Rule):Thread对象的start()方法先行发生于此线程的每一个动作。
6、线程终于规则(Thread Termination Rule):线程中的所有操作都先行发生于对此线程的终止检测,我们可以通过Thread.join()方法结束,Thread.isAlive()的返回值等作段检测到线程已经终止执行。
7、线程中断规则(Thread Interruption Rule):对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生。
衡量并发安全问题一切必须以happens-before 原则为准。

第九章、Class文件结构
Class文件是一组以8字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑排列在class文件中,中间没有任何分隔符,这使得class文件中存储的内容几乎是全部程序运行的程序。这种结构只有两种数据类型:无符号数和表。
魔数:值固定为0xCAFEBABE
最小版本:minor_version
最大版本:major_version
常量池计数器:constant_pool_count
常量池:constant_pool[]
访问标志:access_flags
当前类: this_class
父类:super_class
接口数量:interfaces_count
接口表:interfaces[]
字段数量:fields_count
字段表:fields[]
方法数量:methods_count
方法表:methods[]
属性数量:attributes_count
属性表:attributes[]

属于基本数据类型,主要可以用来描述数字、索引符号、数量值或者按照UTF-8编码构成的字符串值,大小使用u1、u2、u4、u8分别表示1字节、2字节、4字节和8字节。
表:是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有的表都习惯以“_info”结尾。表主要用于描述有层次关系的复合结构的数据,比如方法、字段。

第六章、性能监控工具
显示系统整体资源使用情况:top 命令
显示的参数:
PID:进程ID,进程的唯一标识符
USER:进程所有者的实际用户名
PR:进程的调度优先级。这个字段的一些值是’rt’。这意味这这些进程运行在实时态
NI:进程的nice值(优先级)。越小的值意味着越高的优先级。负值表示高优先级,正值表示低优先级
VIRT:进程使用的虚拟内存。进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES
RES:驻留内存大小。驻留内存是任务使用的非交换物理内存大小。进程使用的、未被换出的物理内存大小,单位kb
SHR:SHR是进程使用的共享内存。共享内存大小,单位kb
S:这个是进程的状态。它有以下不同的值:D - 不可中断的睡眠态;R – 运行态;S – 睡眠态;T – 被跟踪或已停止;Z – 僵尸态
%CPU:自从上一次更新时到现在任务所使用的CPU时间百分比
%MEM:进程使用的可用物理内存百分比
TIME+:任务启动后到现在所使用的全部CPU时间,精确到百分之一秒
COMMAND:运行进程所使用的命令

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值