关注公众号:”奇叔码技术“
回复:“java面试题大全”或者“java面试题”
即可免费领取资料
java之JVM学习记录其中有许多借鉴综合,感谢通哥也感谢大佬们,一起学习加油)
- 关注公众号:”奇叔码技术“
- 1、JAVA的三种常量池字符串常量池,运行时常量池,class文件常量池
- 2、类加载器加载类文件和执行引擎学习过程
- 3、什么是双亲委派机制?简单记忆:我爸是李刚,有事找我爹
- 4、类加载器加载类文件的相关面试问题?
- 5、本地方法栈和本地方法接口:
- 6、Program Counter register程序计算器【PC寄存器】【是CPU硬件】
- 7、JVM第一天总结:
- 8、JVM系统架构
- 9、方法区
- 10、栈管运行
- 11、如果出现java.lang.OutOfMemoryError:java heap space异常,说明JVM虚拟机的堆内存不够。原因有二:
- 12、面试问:jdk还有永久区吗?
- 13、笔试题计算:jdk1.8的新生区、老年区、元空间内存划分
- 14、JVM参数类型:
- 15、标配参数
- 16、X参数
- 17、XX参数 最重要的!!!
- 18、JVM肯定会调优的参数:-Xms -Xmx 这两个是xx参数,不是x参数
- 19、【XX参数,kv调优的值,没有绝对的固定值,都是根据物理内存设置,即运行内存,内存条,RAM】
- 20、 jdk包含jre,jre包含jvm三者关系
- 21、1MB=1024kB,1kB=1024B【1024byte字节】
- 22、Java获取的最大堆内存、Jvm初始化总内存与设置的大小不一致问题
- 23、示例代码:
- 24、JVMGC和全局GC的过程总结
- 25、JVMGC和全局GC的代码示例如图:
- 26、笔试问题:请问怎么判断这个对象是不是一个垃圾,有几种算法?GCRoots对象是哪几个?
- 27、弱引用hashMap和WeakHashMap的区别
- 28、笔试高频:强引用、软引用、弱引用、虚引用的引用类型 特点 使用场景;
- 29、GC算法之复制算法--年轻代的GC
- 30、GC算法之标记清除
- 31、GC算法之标记整理
- 32、GC算法总结:
- 33、垃圾收集器的种类
- 34、java中的STW名词解释
- 35、java中的CMS名词解释
- 36、参数预先声明
- 37、示例控制台
- 38、根据垃圾回收机制,可以知道使用的哪种GC算法进行的垃圾回收机制
- 39、jvm默认垃圾收集器(JDK789)
- 40、笔试题目:关于栈的作用域:
- 41、!!!企业都在用CMS垃圾回收器
- 42、笔试题目:写一下关于老年代CMS收集器的理解和垃圾回收流程?哪些是STW?哪些是并发执行;
- 43、笔试问题:请问怎么判断这个对象是不是一个垃圾,有几种算法?GCRoots对象是哪几个?
- 44、选择合适的垃圾收集器【企业多使用-XX:+UseConcMarkSweepGC 追求低的停顿时间,快速响应】
- 45、常见的OOM【内存不足异常】
- 46、具体的案例分析OOM异常
- 47、通哥实践多线程报错处理方案 线程占用没有被释放也会报【无法创建新的本机线程:错误】
- 48、JVM调优的命令jps和jmap简单使用
- 49、笔试题:说一下 jvm 调优的工具
1、JAVA的三种常量池字符串常量池,运行时常量池,class文件常量池
此外,Java有三种常量池,即**字符串常量池(又叫全局字符串池)、class文件常量池、运行时常量池**。**1. 字符串常量池(也叫全局字符串池、string pool、string literal pool)**字符串常量池 Interned Strings**2. 运行时常量池(runtime constant pool)**当程序运行到某个类时,class文件中的信息就会被解析到内存的方法区里的运行时常量池中。每个类都有一个运行时常量池**3. class文件常量池(class constant pool)**class常量池是在编译后每个class文件都有的,class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是 ***常量池****(constant pool table)*,用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)。*字面量就是我们所说的常量概念,如文本字符串、被声明为final的常量值等。
2、类加载器加载类文件和执行引擎学习过程
请谈谈你对JVM的理解?java8的虚拟机有什么更新?
什么是OOM?什么是StackOverflowError?有哪些方法分析?
JVM的常用参数调优你知道哪些?
谈谈JVM中,对类加载器的认识?JVM的位置:
JVM是运行在操作系统之上的,它与硬件没有直接的交互;
硬件体系 -》 操作系统 -》 JVM通过类加载器,会把特定的类加载到方法区【放类的描述信息,类模板】
classLoader只负责class文件的加载【类似快递员,快递公司又有多个,所以类加载器有多个】
至于是否可以运行,由执行引擎决定;所有的实例,都是通过类加载器加载的类模板而来,每一个实例的地址不同【类似市场的每一瓶水来自同一个矿泉水公司,但每一瓶水都有标识】第一个问题:类加载器有几种?哪几种?作用是什么?
类加载器有四个:jvm虚拟机自带的加载器:三个
启动类加载器(Bootstrap classLoader)C++ 【rt.jar 开始是有java 运行 环境的jar包】
扩展类加载器(ExtClassLoader)extend扩展 【java为了与时俱进就添加了扩展包javac】
应用程序类加载器(AppClassLoader)也叫系统类加载器,加载当前应用的classpath的所有类
注意:自己写的类在这里加载;用户自定义加载器:
java.lang.ClassLoader的子类,用户可以定制类的加载方式sun.misc.Launcher【狼其二】:java程序的入口类【Launcher启动器】抽象类不能实例化,抽象的东西和实例本身就冲突;
3、什么是双亲委派机制?简单记忆:我爸是李刚,有事找我爹
什么是双亲委派机制?什么叫沙箱安全机制?为什么这么干?如果是自定义类型,则会一直往上找,找不到会下沉到子加载器去加载,若不能加载则抛出ClassNotFound异常;当一个类收到了类加载器请求,他首先不会自己加载这个类,而是把他委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在他的加载路径下没有找到所需要的class),子类加载器才会尝试自己去加载;采用双亲委派机制的好处:保证使用不同的类加载可以加载到一样的对象,
沙箱安全机制:同时保证java核心类不会被自定义类所覆盖,出现错误,保证安全性;先到先得最后如果加载之后,由Execution Engine执行引擎负责解释命令,提交操作系统执行;
4、类加载器加载类文件的相关面试问题?
什么是类加载器?有几种类加载器?双亲委派机制?沙箱安全机制?package com.jiqi.jvm;/*** @Description: ${description} // 类说明,在创建类时要填写* @ClassName: ClassLoaderTest // 类名,会自动填充* @Author: 奇迹 // 创建者* @Date: 2021/2/5 10:33 // 时间* @Version: 1.0 // 版本*/
//类加载器
public class ClassLoaderTest {public static void main(String[] args) {Object o = new Object();System.out.println(o.getClass().getClassLoader());//null//getClass() 获取实例的类模板即class字节码文件,//getClassLoader() 获取类模板的类加载器//启动类加载器为C++所写,所有java语言无法加载到,值为nullClassLoaderTest classLoaderTest = new ClassLoaderTest();Class<? extends ClassLoaderTest> aClass = classLoaderTest.getClass();ClassLoader classLoader = aClass.getClassLoader();System.out.println(classLoader);ClassLoader classLoader1 = ClassLoaderTest.class.getClassLoader();ClassLoader classLoader2 = ClassLoaderTest.class.getClassLoader().getParent();ClassLoader classLoader3 = ClassLoaderTest.class.getClassLoader().getParent().getParent();System.out.println(classLoader1);//sun.misc.Launcher$AppClassLoader@18b4aac2System.out.println(classLoader2);//sun.misc.Launcher$ExtClassLoader@1540e19dSystem.out.println(classLoader3);//null//自己写的为应用程序类加载器 加载
/*null
sun.misc.Launcher$AppClassLoader@18b4aac2*/}
}java栈就是存引用地址、局部变量、方法的多线程的六种状态:创建,就绪+运行,堵塞,等待状态,超时等待,终止状态
调用两次start方法会报Thread thread = new Thread();//创建
thread.start();//就绪+运行 不一定马上启动,因为底层是native方法控制得,需要看CPU+系统等硬件的调度;!!!
thread.start();//报错?
5、本地方法栈和本地方法接口:
凡是native方法本地方法java是没有办法控制的,调用的是第三方底层系统或者C语言的库,它会放在本地方法栈【类似于特殊处理,姚明的车子等鞋子需要特殊定制;】
应用:一般使用在硬件方面:程序驱动打印机等因为现在的异构领域间的通信很发达,一般用socket通信或者web Service等就可以满足语言之间的相互调用,java和C语言写的可以相互调用;
6、Program Counter register程序计算器【PC寄存器】【是CPU硬件】
Program Counter register程序计算器【PC寄存器】
方便记忆:PC寄存器就是【排班值日表】每个线程都有一个程序计数器,线程私有,是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,也将要执行的指令台吗),由执行引擎读取下一条指令;
如果是一个native方法,那计数器就是空的;
它是用以完成分支【if-else】、循环、跳转、异常处理、线程恢复等,不会发生内存溢出(OutOfMemor==OOM)错误
7、JVM第一天总结:
1、JVM系统架构图画出来
2、类加载的写法
2.1、有哪几种类加载器
2.2、双亲委派机制
2.3、沙箱安全机制3、native
3.1 native是一个关键字;
3.2声明有,实现无,why?4、PC寄存器
用来记录方法之间的调用和执行情况,类似排班值日表,用来存储指向下一个指令的地址,也即将要执行的指令代码,它是当前线程私有所执行的字节码的行号指示器;5、方法区5.1它存储了每一个类的结构信息5.2方法区是一种规范,不通虚拟机不一样,最典型的是永久代(PermGen space)和元空间(Metaspace)方法区 f = new 永久代方法区 f = new 元空间
6、stack6.1栈管运行,堆管存储6.2栈保存哪些东东?答:
8、JVM系统架构
图中颜色灰色就是:线程私有、不存在垃圾回收机制、内存占用极少;
例如:栈,本地方法栈,程序计数器;图中颜色亮色就是:线程共享,存在垃圾回收机制
例如:方法区,堆
9、方法区
1、供个线程共享的运行时内存区域,
2、存储了每一个类的结构信息;例如:运行时常量池,类模板的【字段、方法数据、构造函数和普通方法】
3、方法区是一种规范,不通虚拟机版本不一样,最典型的是jdk6,7永久代和jdk8元空间but 实例变量在堆内存中,和方法区无关!!!
10、栈管运行
程序=算法+数据结构
程序=框架+业务逻辑队列(FIFO)first input first Output栈(FILO)first input last Output对于栈来说不存在垃圾回收机制,线程私有,线程结束它就结束;
栈内存中分配:8中基本数据类型的变量+对象的引用变量【地址】+实例方法都是在函数的栈内存中分配;java 方法 = 栈帧;1
栈存储什么?
栈帧中主要保存3类数据:
本地变量:输入参数和输出参数以及方法内的变量;
栈操作:记录出栈和入栈的操作;
栈帧数据:包括类文件、方法等;每个方法执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、指向运行时常量池的引用、方法返回地址、动态链接、方法出口等示例代码:报栈内存溢出;
原因:使用递归;
错误代码:java.lang.stackOverflowErrorpublic static void recursiveMethod(){recursiveMethod();}//Exception in thread "main" java.lang.StackOverflowErrorpublic static void main(String[] args) {System.out.println("11");recursiveMethod();System.out.println("44");}
层级结构:
java.lang.Object
java.lang.Throwable
java.lang.Error
java.lang.VirtualMachineError
java.lang.StackOverflowError hotspot:常用的jdk
hotspot是使用指针的方式来访问对象:
java堆中会存放访问【类元数据,描述数据的类,就是方法区中的类模板】的地址,
reference存储的就直接是对象的地址;堆体系结构
一个jvm实例只存在一个堆内存,堆内存的大小-Xms128m[初始堆内存],-Xmx512m【最大堆内存】是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行。1、new新生区
1.1伊甸区
1.2幸存者0区
1.3幸存者1区2、old养老区3、永久存储区【java7】元空间【java8】逻辑上:新生和养老和元空间
物理上:新生和养老新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器回收,结束生命。新生区又分为两部分:伊甸区 和 幸存者区,所有的类都是伊甸区被new出来的,幸存区有两个:0区和1区。当伊甸园的空间用完时,程序有需要创建对象,jvm的垃圾回收机器将堆伊甸园区进行垃圾回收minor GC,将伊甸园区中的不再被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存者0区,若幸存者0区也满了,再对该区进行垃圾回收,然后移动到1区。那如果1区也满了?再移动到养老区。若养老区也满了,那么这个时候将产生MajorGC(FullGC),进行养老区的内存清理。若养老区执行了FullGC之后发现没有空间进行对象的保存了,说明满了,产生的对象数量大于大GC垃圾回收的数量,就会产生OOM异常“OutOfMemoryError”新生区:垃圾回收三次?还是说16次?题目:
在方法中声明的基本数据类型,传给其他方法,传的是复印件,其他方法对其修改不会影响原来方法的基本数据类型的值;
在方法中声明的引用数据类型,传给其他方法,给他是引用地址,其修改的数据,方法实例都是同一个都指向堆内存中,数据都共享,所以,其他方法进行了修改,原来方法的引用数据类型里面的数据也进行了修改;特殊变量:String
String str = "abc"
字符串常量池的原理:没有就新建,有就复用;
11、如果出现java.lang.OutOfMemoryError:java heap space异常,说明JVM虚拟机的堆内存不够。原因有二:
(1)java虚拟机的堆内存设置不够,可以通过参数-xms-xmx来调整;
(2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)
默认新生区会进行轻GC16次,可以进行设置;
还依然存活,就会去养老区永久存储区:是放一些jvm必须的资源,是不会被垃圾回收的;内存溢出OOM不一定是堆内存溢出,内存溢出OutOfMemoryError有很多种
12、面试问:jdk还有永久区吗?
熟悉三区结构后方可学习JVM垃圾收集
实际而言,方法区(MethodArea)和堆一样,是各个线程共享的内存区域,它用于存储虚拟机加载的:类
信息+普通常量+静态常量+编译器编译后的代码等等,虽然JVM规范将方法区描述为堆的一个逻辑部
分,但它却还有一个别名叫做Non-Heap(非堆), 目的就是要和堆分开。 对于HotSpot虚拟机,很多开发者习惯将方法区称之为“永久代(Parmanent Gen)” ,但严格来说,两者有本质的不同,或者说使用永久代来实现方法区而已,永久代是方法区(相当于是一个接口interface)的一个实现,jdk1.7的版本中,
已经将原本放在永久代的字符串常量池移走。
Jdk1.6及之前:方法区的实现是永久代,运行时常量池在永久代中;
Jdk1.7: 方法区的实现是永久代,但已经逐步“去永久代”,常量池在堆中
Jdk1.8及之后:无永久代, 常量池在元空间【堆?】元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。jdk1.8之后最初的永久代取消了,由元空间取代;
目的:将hotspot与Rockit两个虚拟机合并标准;
13、笔试题计算:jdk1.8的新生区、老年区、元空间内存划分
新生区和老年区在堆内存中;新生区 占堆内存三分之一;伊甸区占十分之八;
幸存0区占十分之一;
幸存1区占十分之一;老年区 占堆内存三分之二;元空间:存放在本地内存;
14、JVM参数类型:
15、标配参数
java -version
java -help
16、X参数
-Xint 解释执行 将class文件进行解释;
-Xcomp 【compile】第一次使用就编译成本地代码 将解释后的class文件进行编译成机器的二进制代码
-Xmixed 混合模式 执行和编译同步进行; java version "1.8.0_152"
Java(TM) SE Runtime Environment (build 1.8.0_152-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.152-b16, mixed mode)由此可见:jdk1.8使用的是混合模式【mixed mode】混合的模式,加载的多就编译成机器代码,加载少的就解释执行
17、XX参数 最重要的!!!
boolean类型:
公式:-XX:+加某个属性值 或者 -减某个属性值
+表示开启
-表示关闭
例如:打印GC日志,要么打印,要么不打印;kv设置类型:【XX参数,kv调优的值,没有绝对的固定值,都是根据物理内存设置,即运行内存,内存条,RAM】= 初始化参数设置;
:= 最终的参数设置;【如果进行了修改,就会由=变成:=】
查一下虚拟机某些参数进行了变化,怎么发生的变化; 通过:冒号判断它是否发生过变化
方便在公司查看修改过的参数设置;参数查看命令
-XX:+PrintFlagsInitial 【打印标记初始】 : 查看默认参数设置 -XX:+PrintFlagsFinal 【打印标记最终值】: 最终的参数设置
18、JVM肯定会调优的参数:-Xms -Xmx 这两个是xx参数,不是x参数
-Xms -Xmx 如何解释这两个参数
这两个是xx参数,不是x参数为什么写成Xms 和Xmx ,和sout变成 System.out.println();一样,经常用到,
它实际代表的意义是:-Xms:-XX:InitialHeapSize和-Xmx:-XX:MaxHeapSize比较长,
所以,方便使用就用了缩写-Xms【堆内存初始值,设置初始分配大小,默认为物理内存的 1/64;128?】
-Xmx【堆内存最大值;最大分配内存,默认为物理内存的 1/4;512?】
19、【XX参数,kv调优的值,没有绝对的固定值,都是根据物理内存设置,即运行内存,内存条,RAM】
JDK(Java Development Kit,Java开发工具包)
JDK为Java应用程序提供了基本的开发和运行环境,是整个Java的核心,包括:
1.Java运行环境(JRE)
2.JDK类库,如:java.lang、java.io、java.awt、java.swing等类
3.Java开发工具,如:javac.exe(编译工具)、java.exe(运行工具)、javadoc.exe(生成JavaDoc文档的工具)和jar.exe(打包工具)等与JDK安装目录中的文件夹对应关系,如下图:
bin:java开发工具
jre java运行环境
lib jdk类库(java类库)JRE(Java Runtime Environment,Java运行环境)
JRE是Java程序的运行环境,包含:JVM和Java核心类库JVM(Java Virtual Machine,Java虚拟机)
JVM即Java虚拟机,是整个Java实现跨平台的最核心的部分,是编译后的Java程序(.class文件)和硬件系统的接口,不仅解释执行【JVM参数类型:X参数-Xint】编译【X参数-Xcomp】后的Java指令【jdk8用X参数-Xmixed混合模式】,而且还进行安全检查总的来说:三者之间:JDK什么都有,它包含所有
20、 jdk包含jre,jre包含jvm三者关系
总的来说:三者之间:JDK什么都有,它包含所有JDK【java开发工具包】=JRE+java工具和基础类库
JRE【java运行环境】=JVM+java核心类库【java.lang,jaa.io】
JVM【java虚拟机】运行java语言写的程序,和操作系统交互,硬件交互
21、1MB=1024kB,1kB=1024B【1024byte字节】
因为1kb=1024byte(字节),即1M=1024*1024byte。注:kb是千字节滴意思,其中k表示千,就像M表示兆;b表示字节(byte)。
22、Java获取的最大堆内存、Jvm初始化总内存与设置的大小不一致问题
这两篇文章有争议,哪个是对的???
https://blog.csdn.net/shjfan/article/details/78499644?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.controlhttps://blog.csdn.net/weixin_43649997/article/details/110200947看后面的堆信息:可以知道:最大内存,输出时没计算伊甸园区
初始化内存,输出时没计算一个幸存者区
23、示例代码:
public static void main(String[] args) {//初始化堆内存大小307 *64=19648我的电脑为20G根据运行内存得到,默认是六十四分之一//最大分配内存大小4528 *4=18112我的电脑为20G 根据运行内存得到,默认是四分之一//改变默认的初始化堆内存和最大分配内存大小:-Xms1024m -Xmx1024m -XX:+PrintGCDetails【并打印GC日志】System.out.println(Runtime.getRuntime().totalMemory()/1024/1024);//初始化堆内存大小,//新生区+老年区大小 为 初始化堆内存大小System.out.println(Runtime.getRuntime().maxMemory()/1024/1024);//最大分配内存大小,/*看后面的堆信息:可以知道:最大内存,输出时没计算伊甸园区 设置为:1024 结果为981 伊甸园区为262144K/1024=256 981+256=1137幸存0区或者幸存1区:43520/1024=42.5 42.5+981=1023.5初始化内存,输出时没计算一个幸存者区 设置为:1024 结果为981305664K+699392K=1005056/1024=981.5MB*/
/**981
981
HeapPSYoungGen total 305664K, used 20971K [0x00000000eab00000, 0x0000000100000000, 0x0000000100000000)eden space 262144K, 8% used [0x00000000eab00000,0x00000000ebf7afb8,0x00000000fab00000)from space 43520K, 0% used [0x00000000fd580000,0x00000000fd580000,0x0000000100000000)to space 43520K, 0% used [0x00000000fab00000,0x00000000fab00000,0x00000000fd580000)ParOldGen total 699392K, used 0K [0x00000000c0000000, 0x00000000eab00000, 0x00000000eab00000)object space 699392K, 0% used [0x00000000c0000000,0x00000000c0000000,0x00000000eab00000)Metaspace used 3163K, capacity 4500K, committed 4864K, reserved 1056768Kclass space used 343K, capacity 388K, committed 512K, reserved 1048576K*/}
24、JVMGC和全局GC的过程总结
JVM在GC 的时候,并非每次都对上面三个内存一起回收,大部分回收的指的是新生代。
按照回收区域分为两种:
普通GC(Minor GC)(新生代) 它会回收新生区
全局GC(Major GC)(老年代) 它回收新生区和老年区和元空间【但元空间保存的是jvm必要的资源文件类信息和类加载器,所以不会变,它里面也没有垃圾回收机制】而在java8中移除了永久代,新增了元空间,其实在这两者之间存储的内容几乎没怎么变化,而是在内存限制、垃圾回收等机制上改变较大。元空间的出现就是为了解决突出的类和类加载器元数据过多导致的OOM问题,而从jdk7中开始永久代经过对方法区的分裂后已经几乎只存储类和类加载器的元数据信息了作者:树心图物
链接:https://www.jianshu.com/p/474d98fc4776
25、JVMGC和全局GC的代码示例如图:
[GC (Allocation Failure) [PSYoungGen: 1536K->504K(2048K)] 1536K->704K(7680K), 0.0013801 secs] [Times: user=0.03 sys=0.03, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2006K->495K(2048K)] 2206K->984K(7680K), 0.0007439 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 1979K->416K(2048K)] 3299K->1943K(7680K), 0.0004690 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 1277K->368K(2048K)] 6132K->5223K(7680K), 0.0003716 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) --[PSYoungGen: 1229K->1229K(2048K)] 6084K->6084K(7680K), 0.0008413 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 1229K->0K(2048K)] [ParOldGen: 4855K->2462K(5632K)] 6084K->2462K(7680K), [Metaspace: 3447K->3447K(1056768K)], 0.0035600 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 29K->0K(2048K)] 4156K->4126K(7680K), 0.0002467 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] 4126K->4126K(7680K), 0.0001813 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] [ParOldGen: 4126K->3215K(5632K)] 4126K->3215K(7680K), [Metaspace: 3447K->3447K(1056768K)], 0.0070172 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] 3215K->3215K(7680K), 0.0002657 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] [ParOldGen: 3215K->3195K(5632K)] 3215K->3195K(7680K), [Metaspace: 3447K->3447K(1056768K)], 0.0066655 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Heap
Exception in thread "main" java.lang.OutOfMemoryError: Java heap spacePSYoungGen total 2048K, used 91K [0x00000000ffd80000, 0x0000000100000000, 0x0000000100000000)at java.util.Arrays.copyOf(Arrays.java:3332)eden space 1536K, 5% used [0x00000000ffd80000,0x00000000ffd96e08,0x00000000fff00000)at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674)to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)at java.lang.StringBuilder.append(StringBuilder.java:208)ParOldGen total 5632K, used 3195K [0x00000000ff800000, 0x00000000ffd80000, 0x00000000ffd80000)at com.jiqi.jvm.ClassLoaderTest.main(ClassLoaderTest.java:19)object space 5632K, 56% used [0x00000000ff800000,0x00000000ffb1eda8,0x00000000ffd80000)Metaspace used 3479K, capacity 4500K, committed 4864K, reserved 1056768Kclass space used 378K, capacity 388K, committed 512K, reserved 1048576K
26、笔试问题:请问怎么判断这个对象是不是一个垃圾,有几种算法?GCRoots对象是哪几个?
引用中的:
1、引用计数算法:
每一个对象都会有一个引用计数器,有地方引用该对象就会加1,当引用失效了就会减1;
不管什么时候,只要引用计数器的值为0了,就说明该对象不再被引用了;它就是个垃圾了,可以进行回收;
2、枚举根节点GCRoots有四种:【原理是由GCRoots枚举根节点出发,一直连线不间断的就是被引用的,中断的就是没有被引用的,就是个垃圾,可以进行回收】
1、虚拟机栈(栈帧【方法】中的局部变量区)
2、本地方法栈jni引用的对象【native关键字的方法,C++处理由Bootstrap启动类加载器加载,为null】
3、方法区中的类静态属性引用的对象
4、方法区中常量引用的对象解释1:java在运行的时候,被调用的方法里面的对象肯定就是被引用了,即虚拟机栈的栈帧
解释2:常量池,静态变量的存在中引用的对象即在用
27、弱引用hashMap和WeakHashMap的区别
1、 HashMap<Integer, String> hashMap = new HashMap<>();Integer key = new Integer(1);String value = new String("hello");hashMap.put(key,value);System.out.println(hashMap);key = null;System.gc();System.out.println("-----------");System.out.println(hashMap);
结果:
[GC (Allocation Failure) [PSYoungGen: 1536K->504K(2048K)] 1536K->736K(7680K), 0.0007670 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
{1=hello} 这里!
[GC (System.gc()) [PSYoungGen: 969K->504K(2048K)] 1201K->784K(7680K), 0.0008748 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 504K->0K(2048K)] [ParOldGen: 280K->722K(5632K)] 784K->722K(7680K), [Metaspace: 3445K->3445K(1056768K)], 0.0054777 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
-----------
{1=hello} 这里!只由HashMap更换成WeakHashMap
2、 WeakHashMap<Integer, String> hashMap = new WeakHashMap<>();Integer key = new Integer(1);String value = new String("hello");hashMap.put(key,value);System.out.println(hashMap);key = null;System.gc();System.out.println("-----------");System.out.println(hashMap);
结果:
[GC (Allocation Failure) [PSYoungGen: 1536K->504K(2048K)] 1536K->716K(7680K), 0.0008207 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
{1=hello}
[GC (System.gc()) [PSYoungGen: 908K->504K(2048K)] 1120K->812K(7680K), 0.0005678 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 504K->0K(2048K)] [ParOldGen: 308K->695K(5632K)] 812K->695K(7680K), [Metaspace: 3400K->3400K(1056768K)], 0.0054022 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
-----------
{}总结:如果是弱引用【由java.lang.ref.WeakReference类实现】,且显式的置为null,就会回收;
例如:
HashMap中0的key底层已经由Node数组节点添加了;
WeakHashMap中的key为null之后,它会被回收;引用队列中存放的是垃圾回收的对象
28、笔试高频:强引用、软引用、弱引用、虚引用的引用类型 特点 使用场景;
引用
引用计数算法
29、GC算法之复制算法–年轻代的GC
伊甸区,from区,to区三者GC和存活对象的流程和关系
伊甸区,幸存0区,幸存1区
当伊甸区对象满了,GC之后的存活的对象就会进入幸存区变成from区;另外一个幸存区就是to区当伊甸区对象又满了,就会进行GC,from区也会被GC清除;伊甸区和from区的存活对象,就会进入到to区,to区变成from区,from区变成了to区,并清空伊甸区和to区;from区和to区是来回切换的
from区:有对象的幸存区;
to区:无对象的幸存区;优点:不会产生内存碎片
缺点:
1、会开辟新的空间也就是To survivor【to幸存区】,用来存活对象
2、复制对象会花费一些时间新生区用的是GC算法之复制算法
30、GC算法之标记清除
它会先扫描一次:标记存活对象【1】,未使用【2】,可回收对象【3】;
再扫描一次:清除可回收对象,把第一次扫描的可回收对象回收变成 未使用【2】;缺点:
1、标记和清除效率不高
2、清除之后大量不连续的内存碎片,导致程序运行过程中需要分配较大对象的时候,无法找到足够的连续内存而不得不提前触发一次垃圾收集动作;老年区用的是GC算法之标记清除算法
31、GC算法之标记整理
标记过程和“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存;【就是会整理一下,再清理最边界的可回收对象】优点:
自带整理功能,这样不会产生大量不连续的内存空间,适合老年代的大对象存储;老年区用的是GC算法之标记整理算法
32、GC算法总结:
内存效率:复制算法>标记清除算法>标记整理算法
内存整齐度:复制算法=标记整理算法>标记清除算法
内存利用率:标记整理算法=标记清除算法>复制算法【空了一个to区即幸存区】新生区用的是GC算法之复制算法
老年区用的是GC算法之标记整理【老年串行,老年并行】、GC算法之标记清除【CMS并发 标记 清除 垃圾回收机制】没有最好的算法,只有最合适的算法;
33、垃圾收集器的种类
1、串行垃圾回收器:单线程收集器,在进行垃圾收集的时候,其他线程都必须暂停【STW】,直到回收垃圾结束;不适合服务器环境!!!
类似一个人吃一碗饭的时候,一个阿姨来打扫卫生,你一个人需要等待阿姨扫完卫生后,才能继续吃饭;会占用大量的时间和资源2、并行垃圾回收器:多条垃圾收集器并行工作,和串行垃圾回收器差不多,就是多个几个线程来处理,还是需要等待【STW】;
适用于科学计算,大数据处理等前端交互弱的场景
类似5个人吃一碗饭的时候,分别由5个阿姨来打扫卫生,5个人都需要等待阿姨扫完卫生后,才能继续吃饭;3、并发标记清除(CMS)垃圾收集器:有两段极短的STW【Stop The World】【gc引起的暂停】,和一段并发的CMS4、G1垃圾收集器:综合了三种垃圾收集器,最关键的是采用了可配置在N毫秒内最多只占用M毫秒的时间进行垃圾回收,通过JVM参数
-XX:+UseG1GC 使用G1垃圾回收器查看服务器默认的垃圾收集器:JVM参数:java -XX:+PrintCommandLineFlags[打印命令行标志] -version
默认是-XX:+UseParallelGC【并行GC】
jdk1.7 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
默认是:PSYoungGen 并行垃圾回收器 ParOldGen:老年代并行垃圾回收器jdk1.9 默认垃圾收集器G1例如:
C:\Users\25450>java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=333729600 -XX:MaxHeapSize=5339673600 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC【并行GC】
java version "1.8.0_152"
Java(TM) SE Runtime Environment (build 1.8.0_152-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.152-b16, mixed mode)
34、java中的STW名词解释
Java中Stop-The-World机制简称STW,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)。Java中一种全局暂停现象,全局停顿,所有Java代码停止,native代码可以执行,但不能与JVM交互;这些现象多半是由于gc引起。
35、java中的CMS名词解释
java中的CMS(Concurrent Mark Sweep) 收集器,设计的目的是减少最大的响应时间;
并发 标记 清除Serial【美 /ˈsɪriəl/ c蕊有】:串行;Parallel【 美 /ˈpærəlel/ 胚惹恼】:并行;ParNew:新生区并行
36、参数预先声明
控制台打印的单词所代表的垃圾回收器:
DefNew:Default New Generation 串行垃圾回收器;
Tenured【美 /ˈtenjərd/ 疼利尔得】:old 老年代的意思 老年代串行;
ParNew: Parallel new Generation 新生区并行垃圾回收器
PSYoungGen:: Parallel Scavenge并行垃圾回收器
ParOldGen: Parallel Old Generation 老年代并行垃圾回收器
concurrent mark-sweep generation CMS并发标记清除垃圾回收器
--------------------------------------------------------------------------------------
七种垃圾回收器去除一种的新生区和老年区的分类:新生区:串行垃圾回收器,并行垃圾回收器,新生区并行【ParNew】
---------------------------------------------------G1垃圾回收器【新生和老年都可以用】
老年区:老年串行,老年并行,CMS【并发 标记 清除收集器】推荐搭配使用:
串行+老年串行
新生区并行【ParNew】+CMS
并行+老年串行 / 并行+老年并行重要区别:并行垃圾回收器【 Parallel Scavenge】,新生区并行【ParNew】!!!
37、示例控制台
jdk8默认垃圾回收器【GC】
PSYoungGen 并行垃圾回收器
ParOldGen 老年代并行垃圾回收器HeapPSYoungGen total 2048K, used 76K [0x00000000ffd80000, 0x0000000100000000, 0x0000000100000000)eden space 1536K, 4% used [0x00000000ffd80000,0x00000000ffd932d8,0x00000000fff00000)from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)ParOldGen total 5632K, used 719K [0x00000000ff800000, 0x00000000ffd80000, 0x00000000ffd80000)object space 5632K, 12% used [0x00000000ff800000,0x00000000ff8b3c48,0x00000000ffd80000)Metaspace used 3454K, capacity 4500K, committed 4864K, reserved 1056768Kclass space used 376K, capacity 388K, committed 512K, reserved 1048576K-XX:+UseSerialGC
def new generation 串行垃圾回收器
tenured generation 老年代串行def new generation total 2432K, used 109K [0x00000000ff800000, 0x00000000ffaa0000, 0x00000000ffaa0000)eden space 2176K, 5% used [0x00000000ff800000, 0x00000000ff81b4f8, 0x00000000ffa20000)from space 256K, 0% used [0x00000000ffa20000, 0x00000000ffa20000, 0x00000000ffa60000)to space 256K, 0% used [0x00000000ffa60000, 0x00000000ffa60000, 0x00000000ffaa0000)tenured generation total 5504K, used 714K [0x00000000ffaa0000, 0x0000000100000000, 0x0000000100000000)the space 5504K, 12% used [0x00000000ffaa0000, 0x00000000ffb52970, 0x00000000ffb52a00, 0x0000000100000000)Metaspace used 3383K, capacity 4500K, committed 4864K, reserved 1056768Kclass space used 369K, capacity 388K, committed 512K, reserved 1048576K
38、根据垃圾回收机制,可以知道使用的哪种GC算法进行的垃圾回收机制
新生区用的是GC算法之复制算法
老年区用的是GC算法之标记整理【老年串行,老年并行】、GC算法之标记清除【CMS并发 标记 清除 垃圾回收机制】
39、jvm默认垃圾收集器(JDK789)
jvm默认垃圾收集器(JDK789)
jdk1.7 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)jdk1.9 默认垃圾收集器G1-XX:+PrintCommandLineFlagsjvm参数可查看默认设置收集器类型-XX:+PrintGCDetails亦可通过打印的GC日志的新生代、老年代名称判断JDK1.8新特性:速度更快 – 红黑树
代码更少 – Lambda
强大的Stream API – Stream
便于并行 – Parallel
最大化减少空指针异常 – Optional
40、笔试题目:关于栈的作用域:
基本数据类型的值传到其他栈帧中,代表的是复印件;如果返回值返回并接收了,才是修改了原来main方法的局部变量值;
引用数据类型传地址值,所以进行了set方法,两个方法的指针地址都是指向同一个值,进行了修改,所以main方法输出的是最终修改的值;特殊:String类,引用数据类型,String有字符串常量池,虽然是两个方法指针地址都是指向同一个字符串常量池的值,但是由于String是不可变类,任何对String的改变都会引发新的String对象的生成;所以,如果是重新赋值新的字符串,则另一个栈帧【方法】指向的地址就是另外一个新的字符串地址和值,所以,main方法输出的值还是main方法的栈帧中的字符串常量池里面的值
41、!!!企业都在用CMS垃圾回收器
42、笔试题目:写一下关于老年代CMS收集器的理解和垃圾回收流程?哪些是STW?哪些是并发执行;
CMS concurrent Mark Sweep【并发标记清除】收集器是一种以获取最短回收停顿时间为目标的垃圾收集器,它非常符合在注重用户体验的应用上使用。非常适合堆内存大、cpu核数多的服务器端应用,也是G1出现之前大型应用的首选收集器;
分为四个流程:
1、初始标记:标记 判断对象为引用对象的GCroot中连线能直接关联的对象,此时,需要STW暂定所有工作线程;
2、并发标记:进行GCroot的跟踪过程,和用户线程一起工作,不需要暂停工作线程,主要标记过程,标记全部对象;
3、重新标记:修正在并发标记期间,因用户程序继续运行
4、并发清除
优点:并发收集,停顿低;
缺点:
1、并发执行,对CPU资源压力大
2、采用的标记清除算法会导致大量碎片
43、笔试问题:请问怎么判断这个对象是不是一个垃圾,有几种算法?GCRoots对象是哪几个?
引用中的:
1、引用计数算法:
每一个对象都会有一个引用计数器,有地方引用该对象就会加1,当引用失效了就会减1;
不管什么时候,只要引用计数器的值为0了,就说明该对象不再被引用了;它就是个垃圾了,可以进行回收;
2、枚举根节点GCRoots有四种:【原理是由GCRoots枚举根节点出发,一直连线不间断的就是被引用的,中断的就是没有被引用的,就是个垃圾,可以进行回收】
1、虚拟机栈(栈帧【方法】中的局部变量区)
2、本地方法栈jni引用的对象【native关键字的方法,C++处理由Bootstrap启动类加载器加载,为null】
3、方法区中的类静态属性引用的对象
4、方法区中常量引用的对象解释1:java在运行的时候,被调用的方法里面的对象肯定就是被引用了,即虚拟机栈的栈帧
解释2:常量池,静态变量的存在中引用的对象即在用
44、选择合适的垃圾收集器【企业多使用-XX:+UseConcMarkSweepGC 追求低的停顿时间,快速响应】
1、单CPU或者小内存,单机程序 -XX:+UseSerialGC
【def new generation 串行垃圾回收器 tenured generation 老年代串行】2、多CPU 需要最大吞吐量 ,如后台计算型应用 -XX:+UseParallelGC 或者-XX:+UseParallelOldGC
【PSYoungGen:并行垃圾回收器 ParOldGen:老年代并行垃圾回收器】3、多CPU 追求低停顿时间,需快速响应如互联网应用 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC-XX:+UseConcMarkSweepGC:【par new generation 新生区并行垃圾回收器 concurrent mark-sweep generation CMS并发标记清除垃圾回收器】-XX:+UseParNewGC:不推荐使用,jdk9被移除【par new generation 新生区并行垃圾回收器 tenured generation 老年代串行】
45、常见的OOM【内存不足异常】
java.lang.out of memoryError:java Head space
Java.lang.OutOfMemeoryError:GC overhead limit exceded:程序在垃圾回收上花费了98%的时
间,却收集不到2%的空间,通常这样的异常伴随着CPU的冲高。
Java.lang.OutOfMemeoryError:Direct buffer memory:如果不断的分配本地内存,这时候堆内存
很充足,但本地内存可能已经使用光了,再次尝试分配本地内存,就会出现OOM,程序直接崩溃
Java.lang.OutOfMemeoryError:unable to create new native thread:
导致原因 1 你的应用创建了太多线程了,一个应用进程创建多 个线程超过系统承载极限 2 你的服务器
并不允许你的应用程序创建这么多线程,linix系统默认允许单个进程可以创建的线程数是1024个, 你
的应用创建超过这个教量, 就会报jova. lang. OutofMemoryErroniinable tocreate new native thread
解快办法。
1.想办法降低你应用程序创建线程的数量,分析应用是否真的需要创建这么多线程,如果不是,改代码
将线程数降到最低 2.对于有的应用,确实需要创建很多线程,远超过Linux系统的默认1024个程的限
制,可以通过修改linux服务器配置,扩大inux默认限制
Java.lang.OutOfMemeoryError:MetaSpace:jdk1.8以后有了元空间:虚拟机加载的类信息
(String ...)常量池,静态变量,即时编译后的代码,加载的那些信息,撑爆元空间 就报这个错误
46、具体的案例分析OOM异常
Java.lang.OutOfMemeoryError:Direct buffer memory:如果不断的分配本地内存,这时候堆内存
很充足,但本地内存可能已经使用光了,再次尝试分配本地内存,就会出现OOM,程序直接崩溃例如:类似于初始堆大小为400M,最大堆内存为2G,但实际电脑的运行内存就500M;它运行的数据需要在初始堆大小的基础上增加120M,此时,大于了电脑的运行内存,导致报错;Java.lang.OutOfMemeoryError:unable to create new native thread:
例如:
1、linux系统的默认允许单个线程创建是1024个,使用了太多线程,超过了会报错
2、线程占用没有被释放也会报【无法创建新的本机线程:错误】
47、通哥实践多线程报错处理方案 线程占用没有被释放也会报【无法创建新的本机线程:错误】
Java.lang.OutOfMemeoryError:unable to create new native thread:多个线程中其中有一个线程一直在waitting【等待状态】,结果,自己一个线程一个线程单独跑,发现了,是有个线程里面报了Exception异常,但是处于waitting状态,没有报,这是为什么呢?就是因为提交的方式问题:
线程提交有两种方式:线程池ThreadPool 的submit()和excute()方式执行任务对任务异常的处理
1、因为submit()方法中,对task抛出的异常进行了catch封装,强制将异常捕捉封装并赋值给内置对象,所以没有抛出
2、而execute(task) 方法中并没有捕捉封装,直接将异常抛出
48、JVM调优的命令jps和jmap简单使用
jps : jps是用于查看有权访问的hostspot虚拟机的进程,默认查看本机jvm进程
jmap -heap 70472 【jmap查看程序执行一瞬间的堆使用情况:新生区中的伊甸区和from区【存放没有被引用的垃圾对象】,to区】
from区和to区是来回切换的 from区:有对象的幸存区; to区:无对象的幸存区;还可以在Terminal中输入jps 查看jvm进程,再根据jmap -heap 进程号 查看每一阶段的堆内存使用率;
例如:used = 1258928 (1.2006072998046875MB)
49、笔试题:说一下 jvm 调优的工具
JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具。jconsole:用于对 JVM 中的内存、线程和类等进行监控;
jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等。利用jvisualvm视图监控工具来查看堆内存的占用情况,并对占用堆内存最大的对象进行查看参数和做出相应的修改:
结果查看堆内存的占用,使用gc垃圾回收之后,依然一直居高不下,那么就可以去查看堆Dump,然后查询占用堆内存最大的对象进行查看和修改;
1024*1024=1,048,576 大约1M