目录
5:jvm ,jre , jdk, javase , javaee 都是什么
堆——对象分配过程——TLAB(Thread Local Allocation Buffer)
方法区——方法区在 jdk6,jdk7,jdk8中的面试题(重要)
serial 与 serial old垃圾回收器(串行回收)
parallel Scavenge GC回收器 和 parallel old GC 回收器 (并行回收器,并且是吞吐量优先的垃圾回收器,也是目前jdk8 默认的用的)
parallel Scavenge GC回收器 和 parallel old GC参数设置
G1垃圾回收器(并行回收器)(区域分代优化),(jdk8以后jdk9开始默认的垃圾回收器)
JVM
1:如何查看虚拟机是哪个类型
cmd命令 查看 java -version 查看本地虚拟机版本,我本机安装的是HotSpot
3:特点
- 一次编译,到处运行
- 自动内存管理
- 自动垃圾回收机机制
- JVM就是二进制字节码的运行环境
4: 结构
硬件-----windows/linux 操作系统----JVM虚拟机---- 二进制字节码文件-----java文件
5:jvm ,jre , jdk, javase , javaee 都是什么
- jvm 是虚拟机
- jre包含(基础类库+jvm)
- jdk包含(jre+编译器 ,例如javac,javap)
- javase程序(jdk+IDE工具)
- javaee程序 (javase+应用服务器(tomcat))
6:JVM整体结构
大概流程,和上面一致
命令
编译:javac
反编译:javap -v
查看进程命令:jps
7:类装载器子系统作用(Class Loader)
- Java虚拟机中执行的指令称为字节码指令, 类加载器(Class Loader)是负责将字节码(.class),文件按二进制流的方式装载到方法区的, 然后通过执行引擎(Execution Engine)来解释/ 编译,为对应平台的机器指令后执行的
- 类加载器只负责将字节码文件装载到内存, 至于是否可以运行, 由执行引擎来判断
8:双亲委派机制
当new 一个对象时候,他不会自己先加载,而是先委派请求父类的加载去去执行,父类会委托到最上面的父类,如果最上面父类校验是否在自己的 管辖 内 可以加载成功,就成功返回,若是父类无法完成加载,那么就重新委托给子类去加载,然后子类再去一层层往下去尝试加载,判断当前类是否在自己范围,。 这就是双亲委派机制, 子类先委托父类,父类加载不了,反过委托子类去尝试加载。
- AppClassLoader 系统类加载器 , 加载当前应用的classpath的所有类,或者java.class.path下面的
- ExtClassLoader 扩展包路径 , 加载目录%JRE_HOME%\lib\ext
- BootClassLoader 引导类加载 最顶层 , 主要加载核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class
优势:避免类的重复加载,保护程序安全,防止核心api被恶意篡改
9:沙箱安全机制
优势:跟双亲委派的优势类似,都是堆java核心源代码的保护。
也就是在加载类时候,率先让引导类先加载,然后依次往下子类去加载
10:运行时数据区内部结构
- 程序计数器,栈和本地栈们都是线程私有的,所以是安全的.
- 堆 , 元空间 的线程共享的(1.8以后方法区叫元空间了,1.7方法区叫永久代)
11.程序计数器(pc寄存器)
- pc寄存器是线程私有的
- pc寄存器是jvm中唯一规定不会OOM的区域,也没有GC回收,因为他的指令是替换的,所以内存占用不会一直累加
- pc寄存器是用来存储指向下一条指令地址的,然后由执行引擎读取下一条指令,执行引擎就是把指令转为机器码,然后让cpu去做运算
1:执行一段代码,用javap -v 反编译.class文件,从中看到,寄存器起到的作用:就是存储指令地址(偏移地址),如下图:
12 pc寄存器面试题
13:虚拟机栈
栈管运行,堆管存储
栈是运行时单位,堆是存储时单位
栈解决的是程序如何运行,怎么处理数据;堆解决的是数据存储问题,数据怎么放,放在哪
栈和栈帧都是线程私有的, 堆是线程共享的
栈的组成:
由栈帧组成,并且是线程私有的,每个线程的创建都会创建一个虚拟机栈,其内部方法保存了一个个的栈帧,对应这一次次的方法调用,
也就是说,每个方法都对应一个栈帧,调用时候会进行压栈操作,方法执行完,会进行出栈操作,并且遵循,先进后出的规则
特点:线程私有,每个线程都有一个栈,所以不管多线程情况下,栈都是相互独立的,但是堆内存是共享的,所以,多线程情况下栈和栈帧都是根据线程独立的私有的,但是堆内存是共享的
生命周期:和线程是一样,线程销毁,虚拟机栈也销毁
作用:
主管ava的 程序运行,它保存方法的 局部变量,部分结果,并参与方法的调用和返回
这块强调一下:一要看懂下面的关系
- 局部变量 VS 成员变量
- 8大基本数据类型(int ,double) VS 引用数据类型 (类,接口,数组)
栈由栈帧组成,并且一个方法对应一个栈帧,每个栈帧都对应着一个内存区块
优点特点:
快速有效的分配存储方式,效率仅次于程序计数器
jvm对栈的操作:
- 入栈,压栈
- 出栈
对于栈来说不存在垃圾回收问题?
- 不存在GC
- 会报异常 OOM ,当超出栈内存时候
- 回顾对比 : 程序计数器来说,pc寄存器既不会GC 回收,也不会OOM报异常
如何调整栈的大小:
参数: -Xss1024k ,-Xss 就是设置栈的大小, 我们可以根据实际程序中对栈要求的大小,来调节栈的内存,linux 默认栈内存:1024K,因为栈是线程独立的,所以我们其实在多线程下,是有很多栈的,很占用空间
栈抛出异常:
- java.lang.StackOverflowError 异常
栈帧都弹出栈的方式:
- return ,方法中return 会使方法直接出栈
- 异常,会使方法直接出栈,前提是异常没有被try catch 情况下
- 方法执行完出栈
在idea中跟踪栈信息,开启debugger,然后调用方法
栈帧的内部结构:
栈帧的内存机构组成:
- 局部变量表 8大基本数据类型, 引用数据类型的引用地址,方法的返回类型
- 操作数栈 表达式栈
- 动态链接 指向运行时常量池的方法引用
- 方法返回地址 (方法正常退出或者异常退出的定义)
- 一些附加信息
栈帧 ——局部变量表
局部变量表也成为本地变量表,由数组实现,在类编译时候就确定了数组的长度了。数组里面存局部变量。
存储内容:
- 方法参数
- 方法局部变量(八种数据类型,对象的引用,方法return返回的数据类型)
数据安全问题:
由于局部变量表是存储在线程私有的栈上,所以不会有数据安全问题,
(这个声明一下,如果全局变量的话是有数据安全问题,因为全局变量是存储在方法区中,方法区和堆中是线程共享的数据,所以有安全问题)
周期:
当线程调用的这个方法结束后,局部变量表随之也就销毁
栈帧——操作数栈
操作数栈 其实就是一个 数组实现的,初始化阶段就确定了长度
执行流程
也就是操作栈顶的数据,因为栈顶的栈帧对应的就是当前的方法,所以当方法来回调用,那么栈顶的栈帧也会相应的做压栈出栈的操作,这就是操作数栈的作用。
总结 :操作当前的栈帧对应方法的入栈和出栈。
周期
随着栈帧的销毁二销毁,也就当前调用的方法执行完以后,就销毁
栈帧——栈顶缓存技术
就是将栈顶元素,全部缓存到物理机的cpu的寄存器中,以此降低对内存的读取次数,提升执行引擎的执行效率
栈帧——动态链接(指向运行时常量池的地址)
作用
是将符号指向运行时常量池的地址,注意运行时常量池是存放在方法区中
示例:当我们对一个。class 文件 用 javap -v 。class 命令反编译后
栈帧——方法调用
子类对象的多态的使用前提:1:类的继承关系 2:方法的重写
虚拟机栈的五道面试题
本地方法接口和本地方法库
了解即可。。。。什么是本地方法:就是用Native 修饰的方法,就是本地方法,因为本地方法要去调用一些类似c或者其他语言编写的接口,所以就需要用native修饰的方法去调用
本地方法栈
- 虚拟机栈是管理java方法的调用,本地方法栈是管理本地方法的调用
- 本地方法栈也是线程私有的,不会被GC回收,但是会报 StackOverFlowError 栈内存溢出异常
堆
组成部分:
- jdk 1.7 以前: 年轻代,老年代 ,永久代 ,其中(年轻代包括 eden区,幸存者S0区,幸存者S1区)
- jdk1.8 : 年轻代 ,老年代,元空间
注意:
元空间 逻辑上属于 堆空间,但是在实际存放时候,并没有将元空间内存算入到堆中,而是元空间算在了方法区中
调整堆内存大小参数
-Xmx1024m(堆最大值) -Xms1024m(堆初始值) 两个值在生产环境中尽量设置一致
-Xmn256m (设置年轻代内存大小(=Eden区+幸存者S0+S1区))
默认的堆内存大小:
- 初始值:物理电脑内存 / 64
- 最大值:物理电脑内存 / 4
开发中的参数设置
-Xmx(堆最大值) -Xms(堆初始值) 这两个参数最好设置相同的值,因为避免设置不一样值以后,频繁的gc,频繁的扩容初始值,从而导致降低系统性能
新生代与老年代在堆结构的占比 默认:1:2, -XX:NewRatio=2 ,通过这个参数这个设置这个占比
默认 -XX:NewRatio=2 ,,表示新生代占比是1 ,老年代占比是2,新生代占整个堆的1/3
可以修改为 -XX:NewRatio:5 ,,表示新生代占比是1,老年代占比是5,新生代占整个堆的1/6
其实我们一般不会改变整个占比,但是如果在程序执行中,对象的生命周期比较长的话,那么我们就可以增大老年代
新生代中Eden区和幸存者S0和S1的占比 默认8:1:1,-XX:SurvivorRator=8 ,通过这个参数可以设置这个占比
默认 -XX:SurvivorRator=8 , 表示新生代中eden区,与 幸存者S0与S1的占比是 : 8:1:1 ,也可以去手动调整这个比例,但是呢,这个默认设置不太好用,还是需要我们手动在参数中进行设置
其中这个默认参数不起作用的,原因是我们jvm中默认开启了 自适应内存内存分配策略,我们可以把自适应内存分配策略关掉,
自适应内存内存分配策略 默认是 -XX:+UseAdaptiveSizePolicy 开启的,我们可以关掉这个设置-XX:-UseAdaptiveSizePolicy
就是 -XX:-UseAdaptiveSizePolicy 减号就是关闭自适应内存分配策略,-XX:+UseAdaptiveSizePolicy 加号就是开启这个策略,默认是开启了这个策略,那么为什么关掉策略还不好用呢,后续会在说这个。。。。
java中几乎所有new的对象都存放在jvm的堆中
问题1?调用一个方法,方法中引用了一个对象作为参数,对象的引用地址在栈内存中存储,对像的内存存在堆中,但是当方法执行结束,也就是对象引用地址销毁后,为什么指针指向的堆内存的那个对象步不gc清除掉??
————回答:首先,当栈内存的对象引用地址销毁后,堆中的对象并不会立刻被gc回收,是由于因为有可能还有别的方法在引用这个对象,所以不可能频繁的去gc对象,而且gc原理是,当堆内存不够存储对象时候才去gc,会把一些没有指针引用的对象给清除掉。
出现异常
java.lang.OutOfMemoryError
调试工具
cmd命令行,输入--- jvisualvm
1:概述中,有jvm整个的参数
2:监视中。可以查看堆快照
3:抽样器中我们可以实时看到,堆的内存情况,
4:VisualGc中,可以看到整个回收的全貌
堆-的对象分配过程——一般过程
1:基本上80%的对象都被eden区的GC回收
2: 频繁回收新生代,很少回收老年代,几乎不动元空间
常用的调优工具
堆——MinroGC ,majorGC 、 FullGC
1: MinroGC (YGC) 年轻代GC,这块要注意在MinroGc时候会先判断old区的空间是大于这个新生代对象的总大小,如果大于,那么minorGC,如果小于那么majorGC/ fullGc
2: majorGC 主要针对回收堆种old区的空间,但是每次majorGc,都会伴随MinroGC
3: FullGC 回收堆和方法区的空间
4: 当进行GC回收时,用户线程是停止的,只有当GC线程结束后,用户线程才可以使用
5: majorGC / FullGC 花费时间 大概是 MinroGC 的十倍,所以要减少majorGC 和 fullGc的次数
6:新生代中From区幸存者区的标记当达到15以后,就会转到老年去,但是当eden区的对象要放到幸存者区,但是幸存者区放不下了,就会根据动态对象年龄判断,把一部分标记了的但是并没有达到15的幸存对象直接放到old区,
,动态对象年龄判断,当每次minorgc时候,eden区和from区都进行GC,把没有使用的对象进行垃圾回收,当时回收完以后判断 from区中幸存对象的年龄,当S0区的同等年龄的对象的内存大于S0的空间的一半,那么下次GC时候,直接把这个年龄及以上的直接放入老年代old区。
-
堆——对象分配过程——TLAB(Thread Local Allocation Buffer)
堆区是共享区域,任何线程都能访问到堆区的共享数据,那么就会造成数据不安全问题,如何解决呢?
什么是TLAB
从内存模型角度去分析:Eden区继续进行划分,JVM每个线程都划分一个私有缓存区域,它包含在Eden区内
优点
多线程分配内存同时,可以避免多线程造成的数据的安全问题,同时还能够提升系统的吞吐量,我们把这个叫做快速分配策略
设置是否开启TLAB空间参数,默认是开启的
-XX:UseTLAB ,-XX:+UseTLAB ,是加号就是开启 TLAB, -XX:-UseTLAB的话就是关闭TLAB状态
占Eden空间比,默认是Eden区的1%
默认情况下仅占用Eden空间的1%,当然我们也可以手动设置TLAB的占用空间,参数:-XX:TLABWastTargetPercent 来设置TLAB的占比
分配TLAB失败,jvm使用加锁机制保证数据安全
但分配TLAB内存失败时,JVM会使用 加锁 机制 来保证多线程下数据的安全性的。
堆——堆空间参数的总结(重要,面试中常问的)
堆——逃逸分析——代码优化
逃逸分析,1.7以后jvm默认开启逃逸分析
当一个ava方法中声明了一个局部变量 (引用类型的变量 ,例如对象)List list = new ArrayList(),如果此局部变量(引用类型的变量 ,例如对象)只是在当前方法中使用,就有可能是没有逃逸,
但是当给return 出方法以后,或者在内部赋值给别的成员变量以后,那么就被认定逃逸,也就是有可能被别的方法引用。
好处:
- 没有逃逸的一些成员变量(引用类型的变量 ,例如对象),直接存储在栈的栈帧空间内,当方法执行完,会随着线程销毁而被销毁
- 当逃逸的一些成员变量(引用类型的变量 ,例如对象),这些变量由于逃逸方法外了,所以存储在堆空间上,所以需要GC才能回收这些变量,所以大量这样变量会导致jvm频繁GC,系统性能下降
优化:
所以我们在方法内部定义变量时候,尽量在方法内部使用,不要return返回,或者给成员变量赋值
标量替换,默认是开启的
就是当一个方法内的局部变量是一个参数时候,在没有逃逸的情况下,那么开启标量替换 ,jvm会把这个对象打散为对象中的一个个属性变量,以对象的属性变量作为变量存储在栈中,而不在堆中存储对象
同步省略
当方法内的一个局部变量参数,作为一个锁持有的对象的时候,jvm为了不影响执行效率,会根据同步策略使锁消除, 因为这个锁加的使没有意义的, 这种也叫 锁消除
方法区(1.7以前叫永久代 ,1.8后叫元空间)
----方法区可以看作是一块独立于堆的内存,也可以叫做非堆
----方法区的大小决定了可以存放多少个类
---每次FullGC都会对方法区也进行垃圾回收
那么当我们Student stu = new Student(); new了一个对象,我们分析一下,他的各个存放在哪个位置?
- Student 存放在 方法区,因为他是一个对象类型,方法区就是存放所有的对象类型
- stu 存放在 栈内存,因为stu是声明的引用数据变量,栈中存局部变量,和引用数据变量的内存地址
- new Student(),这个存放在堆中,因为 几乎所有的new 对象都存放在 堆区中,(这里注意jvm的逃逸分析,有可能也不存在堆中)
在什么情况下方法区(元空间)的内存容易溢出呢?
- 加载了大量的第三方jar包
- tomcat部署的工程太多,(30--50个)
- 大量的生成反射类
总之一句话,就是一次性加载了太多的类的话,就有可能报方法区的OOM异常
方法区如何设置内存参数,并且依据什么去设置初始值的大小
方法区——内部结构
方法区存储内容:
方法区——运行时常量池
常量池:
运行时常量池:
方法区——方法区在 jdk6,jdk7,jdk8中的面试题(重要)
方法区——GC回收
方法区——常见面试题
对象的实例化
对象创建六个步骤
对象内存布局
执行引擎的作用及操作过程
主要是把字节码指令翻译成机器指令,给cpu识别运行, 执行引擎依赖与pc寄存器,
每执行一条指令,需要pc寄存器去找下一条指令地址,然后翻译指令为机器码,给cpu识别
执行引擎工作过程
执行引擎——解释器
为什么说java是半编译半执行的语言,
是因为jvm在执行java代码时候,会用解释器和jiti编译器结合使用
执行引擎——JIT 即时编译器
String基本特性(重要:面试题)
== 比较的是地址
equals比较的是值
String——intern()
使-用intern 方法后,会把数据存放在堆中的字符串常量池中,还会确保只有一份,如果常量池中存在,就放在堆中,如果常量池不存在,那么就放在常量池中,,所以节约内存,节省效率
String——字符串拼接new了几个对象(面试题)
String——return 面试
垃圾回收机制
没有任何指针指向的对象就是垃圾
内存溢出
被无用的对象沾满内存导致的溢出 ,就是内存溢出
内存泄漏
程序运行中,被有用的对象一直占用,直到沾满内存,导致的溢出,就是内存泄漏
垃圾回收相关算法
垃圾回收相关算法——标记阶段
垃圾回收相关算法——标记阶段——引用计数法
垃圾回收相关算法——标记阶段——可达性分析算法
垃圾回收相关算法——标记阶段——finalzatize
jprofiler的使用
垃圾回收相关算法——清除阶段
标记清除算法
复制算法
幸存者区,用的就是复制算法
标记压缩算法
三种算法对比
分代收集算法
增量收集算法
分区算法
垃圾回收相关概念
垃圾回收相关概念——内存溢出与内存泄漏
STOP THE WORD (简称STW)
垃圾回收的并行和并发
安全点和安全区域
java中几种不同引用
强引用
软引用
弱引用
虚引用
垃圾回收器
GC分类与性--能指标
评估GC性能指标——吞吐量/和 暂停时间
几种垃圾回收器
垃圾收集器的组合关系
如何 查看默认的垃圾回收器
jdk8默认的时 parallel Scavenge GC + parallel old GC
jdk9默认是G1
serial 与 serial old垃圾回收器(串行回收)
parNewGc回收器(并行回收)
parallel Scavenge GC回收器 和 parallel old GC 回收器 (并行回收器,并且是吞吐量优先的垃圾回收器,也是目前jdk8 默认的用的)
parallel Scavenge GC回收器 和 parallel old GC参数设置
CMS回收器:低延迟 (并发收集器)
G1垃圾回收器(并行回收器)(区域分代优化),(jdk8以后jdk9开始默认的垃圾回收器)
优势和不足
7种垃圾回收器总结(面试)
常用显示GC日志的参数(面试)
参数分析对照表
GC日志分析工具 GCEsay,直接打开网站即可
先用数据日志到文件的命令,把文件输入出来,我们再用GCesay去分析