java进阶视频(java进阶视频教程)

作者:电脑培训网 2024-04-30 07:46:07 902

源创散文集|【进阶】Java高级JVM实战

文章目录

前言1.面试题解析2.JVM理论详解JVM的位置JVM架构类加载器3.JVM双亲委托机制4.Native关键字5.PC寄存器和方法区6.栈和堆7.JVM的三种类型,新区、旧区、永久区总结前言

java进阶视频(java进阶视频教程)

JVM是Java实现跨平台的基础。所有的Java程序都是基于JVM的,那么JVM底层是如何实现的呢?Java已经流行了20多年。让我们来看看这个强大的JVM吧!

一、面试题解析

以下为面试常见问题

您能谈谈您对JVM的理解吗?Java8虚拟机和之前的改动更新了吗?

JVM:虚拟机,源文件.java在虚拟机中被编译器编译成字节码文件.class,是整个java跨平台实现的核心部分。

Java8虚拟机取消了永久代,引入了元空间的概念。

在HotSpot虚拟机中,在jkd1.6中,设计团队将方法区设计为永久代,以便可以将GC工作区扩展到方法区。这种策略可以避免为方法区设计单独的垃圾回收机制,但缺点是方法区的回收条件非常苛刻,回收效果不好。

在最新的Java1.8中,永久代已经被取消,改为元空间。

元空间规则:

元空间中的类及其相关元数据与类加载器的生命周期一致。每个类加载器都有专用的存储空间,不会单独回收某个类。位置也是固定的,但是当类加载器不再存活时,它的所有相关空间都会被移除。

什么是OOM?什么是堆栈溢出错误?怎么分析呢?

OOM:内存溢出。原因是发生了一些事情导致程序使用了大量的jar和类,导致Java虚拟机内存空间不足。它与永久代空间相关。

解决方案:

增加Java虚拟机中XX:PermSize和XX:MaxPermSize参数的大小。XX:PermSize是初始永久存储区域大小,XX:MaxPermSize是最大永久存储区域大小。清理应用程序中web-inf/lib下的Jar或者Maven仓库中的Jar,防止大量Jar文件导致程序崩溃。StackOverflowError:当堆栈深度超过虚拟机分配给线程的堆栈大小时,就会发生堆栈溢出。错误。

注意:堆栈已满只是由于递归错误。无限循环一般不会占用更多的内存或者特定的Stack,而只占用CPU,所以不会抛出这个错误。

分析:抓取内存快照并分析转储文件。

常用的JVM调优参数有哪些?

-Xms2g:初始推送大小为2g;-Xms2g:最大堆内存为2g;-XX:NewRatio=4:设置年轻代和老年代的内存比例为1:4;-XX:SurvivorRatio=8:设置新一代Eden和Survivor的比例为8:2;-XX:+UseParNewGC:指定使用ParNew+SerialOld垃圾收集器组合;-XX:+UseParallelOldGC:指定使用ParNew+ParNewOld垃圾收集器组合;-XX:+UseConcMarkSweepGC:指定使用CMS+SerialOld垃圾收集器组合;-XX:+PrintGC:启用gc信息的打印;-XX:+PrintGCDetails:打印GC详细信息;如何捕获内存快照以及如何分析转储文件?

启用内存快照。当OOM发生时,转储文件将自动放置在不同的路径中。

-XX:+HeapDumpOnOutOfMemoryError#将内存快照放到指定路径-XX:HeapDumpPath=/usr/local/app/oom

最重要的是打印出GC日志。使用jstat工具分析GC频率和性能时,可以使用GC日志。jstat可以分析GC频率,但是对于每种具体的GC情况,可以结合GC日志来分析。

谈谈你对JVM中类加载器的理解?

ClassLoader是Java语言的一项创新,也是Java流行的重要原因。在类加载的第一阶段,即“加载”过程中,需要通过类的完全限定名来获取定义该类的二进制字节流。完成这个动作的代码块就是类加载器。这个动作是在Java虚拟机之外实现的,以便应用程序可以决定如何获取所需的类。

类加载器最重要的是双亲委托模型,下面将对此进行解释。

二、JVM理论详解

JVM的位置

JRE中的JVM

JVM的体系结构

类加载器

虚拟机自带的加载器启动类加载器扩展类加载器应用程序加载器三、JVM双亲委派机制

/*双亲委托机制1.类加载器接收类加载Application的请求2.加载这个请求向上委托给父类加载器完成,并继续向上直到根加载器3.根加载器检查是否可以加载当前类。如果可以加载,则结束。如果无法加载,则会抛出异常并通知子加载器加载。4、重复步骤3。如果直到加载完成还没有找到,就会抛出ClassNotFound!null:java无法调用的loader是因为底层是C++编写的,调用了C++的本地栈方法,所以为null*/

装载顺序

包java.lang;publicclassString{//双亲委托机制:安全,网上一层层查找,有的话就上面的,没有的话从根开始一层层查找,直到找到为止//1.APP---EXC---BOOT//BOOT不可用---EXC不再可用---APP找到了!@OverridepublicStringtoString(){return'HelloWorld!';}publicstaticvoidmain(String[]args){Strings=newString();System.out.println(s.toString());}}

我们新建了一个java.lang.String类,加载的时候会报错。为什么?就是因为双亲委派机制,直接调用ROOT下的String类。

很好的解释了父委托机制

沙盒安全机制

很好的解释了沙盒安全机制

就明白了

四、Native关键字

Native是一种计算机功能,NativeMethod是Java调用非Java代码的接口。该方法的实现是用非Java语言实现的,例如C或C++。

我们知道,当一个类第一次被使用时,这个类的字节码会被加载到内存中,并且只会被加载回来一次。在加载的字节码的入口处维护了该类的所有方法描述符的列表。这些方法描述符包含这样的信息:方法代码存储在哪里、有哪些参数以及方法描述符。类)等。

如果方法描述符包含native,则描述符块将有一个指向该方法的实现的指针。这些实现在一些DLL文件中,但是它们会被操作系统加载到Java程序的地址空间中。当加载具有本机方法的类时,不会加载其关联的DLL,因此不会设置指向方法实现的指针。这些DLL只有在调用本机方法时才会被加载,这是通过调用java.system.loadLibrary()来实现的。

最后要注意的是,使用本机方法会产生开销,并且失去了Java的许多优点。如果我们没有选择,我们可以选择使用本地方法。

Thread类调用本机方法来启动线程。

//navive:任何带有native关键字的方法,表示无法到达Java的作用域,会调用C语言库//会进入本地方法栈//Java诞生时,C和C++风靡全球,Java想要立足,必须有调用C和C++的程序//JNI的作用:扩展Java程序的用途,为Java、C/C++集成不同的编程语言//开启为本地方法栈开辟一块内存区域,用于注册需要执行的本地方法//最终执行时,通过JNI加载本地方法库中的方法privatenativevoidstart0();//调用其他语言的接口,http、Socket、WebService

掌握就好,在企业级应用中比较少见!

五、PC寄存器与方法区

PC寄存器

程序计数器:程序计数器寄存器

每个线程都有一个程序计数器,该计数器是该线程私有的。它是一个指向方法区中方法字节码的指针。执行引擎读取下一条命令,这是非常小的内存空间,几乎可以忽略不计。

方法区

方法区方法区

方法区被所有线程共享。所有的字段和方法字节码,以及一些特殊的方法,比如构造函数、接口代码也都在这里定义。简单来说,所有定义的方法的信息都保存在这个区域中。该区域属于共享区间

静态变量、常量、类信息、运行时常量池存在于方法区中,但实例变量存在于堆内存中,与方法区无关。

六、栈与堆

堆栈

栈是一种数据结构

程序=数据结构+算法

栈:先进、后出、桶

队列:先进先出,先输入先输出

喝多了就会吐,吃多了就会拉屎,就会变成队列。

简单的执行流程

公共类测试{公共静态voidmain(String[]args){newTest().test();}公共无效测试{}}

内存图中的结构如下

公共类测试{公共静态voidmain(String[]args){newTest().test();}publicvoidtest(){a()}publicvoida(){test();}}

这将以无线方式堆叠堆栈,直到堆栈内存溢出。

这种错误是非常严重的。例如,我们在递归时经常会看到这种错误。一旦发生,程序OOM就很难解决和避免。

内存中对象的创建过程

公共类Person{私有整数noid;私有字符串名称;私有整数年龄;publicvoidinfo(){System.out.println('学生姓名:'+name);System.out.println('学生年龄:'+年龄);System.out.println('学生的noid:'+noid);}publicstaticvoidmain(String[]args){//实例化对象Personperson=newPerson();//给属性赋值person.name='小智';人.年龄=20;人.noid=1;//调用方法person.info();}}

这段代码有3个成员变量和1个成员方法,我们给它们赋值

内存结构图

在Java内存中,有3个用于创建对象的区域,栈区、堆区和方法区。

将类信息和成员方法加载到方法区,并将成员属性加载到堆中

main函数进入栈区并定义一个指向Person类实例的Person类型引用,并在堆区创建Person对象的实例。

接下来,进行赋值操作。现在在栈区找到对象的引用,然后根据引用将值赋给堆区。

然后调用info方法,首先在栈区找到对象的引用,然后根据指针去堆区找到实例,然后再去方法中调用方法

该方法最后执行。方法执行完毕后,方法被弹出,也称为从栈中弹出。最后弹出main函数。

至此,在内存中创建对象的过程就完成了。

堆,一个JVM只有一种堆内存大小,并且堆内存大小是可以调整的。

类加载器读取类文件后,通常会往堆上放什么?方法、全局变量,保存着我们引用的真实对象

堆内存也细分为3个区域

新区年轻/新老年区老永久区Perm

GC垃圾回收主要集中在伊甸园和退休区。

假设内存满了,就会出现OOM错误,堆内存不够了。

导入java.util.Random;publicclassTest{publicstaticvoidmain(String[]args){Stringstr='';while(true){str+=str+newRandom().nextInt(666666666)+newRandom().nextInt(99999999);}}}

JDK8之后永久存储区域改为元空间

七、三种JVM、新生区、老年区、永久区

以下三个JVM

HotSpotBEAJRockitIBMJ9VM我们使用HotSpot

新生活区

阶级:出生、成长、甚至死亡的地方都在伊甸园。所有对象都位于幸存者区域(0,1)中,该区域是伊甸公园中的新区域,并在老年区域之间来回切换。

在老年区,经历过无数次GC收集而尚未死亡的人会进入老年区。进入老年区的物品很少。

真相:经研究,99%的物品都是临时物品,都在伊甸园里,使用后就会丢失。

永久区域

该区域常驻内存,用于存储JDK本身携带的Class对象。Interface存储元数据,存储Java运行时的一些环境或类信息。该区域不被GC收集为垃圾。当虚拟机关闭时,该区域的内存将会被释放。

一个启动类会加载大量的第三方jar包。Tomcat部署的应用程序过多,不断加载大量动态生成的反射类,直至内存满,就会出现OOM。

jdk1.6:永久代之前,常量池在方法区jdk1.7:永久代,满了退化,去堆里的永久代常量池。jdk1.8之后:元空间内没有永久代、元空间、常量池

逻辑上存在,物理上不存在

一个项目中,突然出现了OOM故障,那么如何排除并研究为什么会出错~

可以看看哪行代码有错误:快照分析工具,MAT:Eclipse工具JProfilerDeBug,逐行分析MAT和Jprofiler的功能:

分析Dump内存文件,快速定位内存泄漏,获取堆中错误,获取大对象~小结

以上是【Bug终结者】到【进阶】Java高级JVM实践的简单介绍,JVM是Java必备高级准备。在项目开发中,如果熟练掌握了JVM,你会感觉无比的好。为了提高程序QPS和吞吐量,JVM调优是必要的。也是高频的Java高级面试。可见,掌握并灵活使用JVM可以说已经达到了高级水平!

如果这篇【文章】对您有帮助,希望您能给【Bug终结者】点赞。创作并不容易。如果你对【后端技术】和【前端领域】感兴趣,也欢迎你关注【Bug终结者】,我会给你带来巨大的【收获和惊喜】!

相关推荐