猿创征文 |【高级篇】Java 进阶之JVM实战

news2024/11/20 20:37:56

文章目录

  • ⚡前言
  • 一、面试题解析
  • 二、JVM 理论详解
    • ⛅JVM的位置
    • ❄️JVM的体系结构
    • ⏳类加载器
  • 三、JVM 双亲委派机制
  • 四、Native 关键字
  • 五、PC寄存器 与 方法区
  • 六、栈与堆
  • 七、三种JVM、新生区、老年区、永久区
  • ⛵小结

⚡前言

JVM 是 Java 实现 跨平台的基础,所有的Java 程序都基于JVM,那么JVM底层到底是如何实现的呢,Java目前已火了20多年了,下面我们就一起来看看 这个强大的 JVM!!!

一、面试题解析

以下是面试高频题

请你谈谈你对JVM的理解?Java8虚拟机和之前的变化更新?

JVM(Java Virtual Machine):虚拟机 ,源文件.java在虚拟机中通过编译器编译成字节码文件.class,是整个java实现跨平台的最核心的部分

Java 8 虚拟机 撤销了 永久代,引入了 元空间的概念。

在HotSpot虚拟机中,jkd1.6时,设计团队把方法区设计为永久代,这样GC工作区域就可以扩展至方法区。这种策略可以避免为方法区单独设计垃圾回收机制,但是坏处就是,方法区的回收条件十分苛刻,而且回收效果也不好。

到了最新的Java1.8 ,撤销了永久代,改为了元空间。

元空间的规则:
元空间中类及其相关的元数据和类加载器生命周期一致,每个类加载器有专门的存储空间,不会单独回收某个类,位置也是固定的,但是当类加载器不再存活时会把它相关的空间全部移除。

什么是OOM?什么是栈溢出StackOverflowError?怎么分析?

OOM(OutOfMemoryError):内存溢出,原因是发生了某种原因 导致程序使用了大量的jar 和 class,使Java虚拟机的内存空间不足,与Permanent Generation space有关

解决方案:

  1. 增加Java虚拟机中的 XX:PermSizeXX:MaxPermSize参数的大小,XX:PermSize是初始永久保存区域大小,XX:MaxPermSize是最大永久保存区域大小。
  2. 清理应用程序中web-inf/lib下的 Jar 或者 Maven仓库中的Jar,防止出现大量的Jar文件导致程序崩溃

StackOverflowError: 栈溢出,当栈深度超过虚拟机分配给线程的栈大小时就会出现此Error

注意 递归的错误,才出现Stack满的情况,无限循环一般不会占用更多的内存或者具体的Stack,只是占cpu而已,所以不会抛此错误。

分析:抓取内存快照,分析Dump文件。

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

  • -Xms2g:初始化推大小为2g;
  • -Xms2g:堆最大内存为2g;
  • -XX:NewRatio=4:设置年轻的和老年代的内存比例为1:4;
  • -XX:SurvivorRatio=8:设置新生代Eden和Survivor比例为8:2;
  • -XX:+UseParNewGC:指定使用ParNew + Serial Old垃圾回收器组合;
  • -XX:+UseParallelOldGC:指定使用ParNew + ParNew Old垃圾回收器组合;
  • -XX:+UseConcMarkSweepGC:指定使用CMS + Serial Old垃圾回收器组合;
  • -XX:+PrintGC:开启打印gc信息;
  • -XX:+PrintGCDetails:打印gc详细信息;

内存快照如何抓取,怎么分析dump文件?

开启内存快照,当出现OOM时,会自动将dump文件放到改路径下

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

最重要的是要打印出来GC日志GC日志可以配合你用jstat工具分析GC频率和性能的时候用,jstat可以分析出来GC的频率, 但是对每次具体的GC情况,可以结合GC日志来看

谈谈JVM中类加载器你的认识?

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

类加载器最重要的就是双亲委派模型,在下方会有说明。

二、JVM 理论详解

⛅JVM的位置

JVM在JRE中
在这里插入图片描述

❄️JVM的体系结构

在这里插入图片描述

⏳类加载器

在这里插入图片描述

  1. 虚拟机自带的加载器
  2. 启动类(根)加载器
  3. 扩展类加载器
  4. 应用程序加载器

三、JVM 双亲委派机制

/*
    双亲委派机制
    1.类加载器收到类加载的请求    Application(应用加载)
    2.将这个请求向上委托给父类加载器去完成,一直向上委托,直到根加载器
    3.根加载器检查是否能够加载当前这个类,能加载就结束,不能加载就抛出异常,通知子加载器进行加载
    4. 重复步骤3. 直至完成加载
    如果都没找到,则会抛出ClassNotFound!

    null:java调用不到的加载器,是由于底层是由C++写的,调用的C++的本地栈方法,所以是null

 */

加载顺序

package java.lang;

public class String {

    //双亲委派机制:安全,一层一层网上找,上面有就使用上面的,没有就从根部一层层外外找,直至找到为止
    //1. APP(应用加载)  ---> EXC(扩展加载)  ---> BOOT(根加载,最终执行)
    //BOOT没有 --> EXC 再没有  ---> APP找到!
    
    @Override
    public String toString() {
        return "Hello World!!!";
    }

    public static void main(String[] args) {
        String s = new String();
        System.out.println(s.toString());
    }
}

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

很好的双亲委派机制讲解

沙箱安全机制

很好的沙箱安全机制讲解

了解即可

四、Native 关键字

native是一个计算机函数,一个Native Method就是一个Java调用非Java代码的接口。方法的实现由非Java语言实现,比如C或C++。

我们知道,当一个类第一次被使用到时,这个类的字节码会被加载到内存,并且只会回载一次。在这个被加载的字节码的入口维持着一个该类所有方法描述符的list,这些方法描述符包含这样一些信息:方法代码存于何处,它有哪些参数,方法的描述符(public之类)等等。

如果一个方法描述符内有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++
//它在内存区域开辟了一块空间为本地方法栈(Native Method Stack) 来登记需要执行的本地方法
//在最终执行的时候,通过JNI加载本地方法库中的方法
private native void start0();

//调用其它语言的接口,http、Socket、WebService

掌握即可,企业级应用中较为少见!

五、PC寄存器 与 方法区

PC寄存器
程序计数器:Program Counter Register

每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向像一条指令的地址,也指向即将要指向的指代代码),在执行引擎读取下一条命令,是一个非常小的内存空间,几乎可以忽略不计

方法区

Methad Area 方法区

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

静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是实例变量存在于堆内存中,和方法区无关

六、栈与堆

栈是一种数据结构

程序=数据结构+算法

栈:先进后出,桶

队列:先进先出(FIFO),First Input First Output

喝多了吐就是栈,吃多了拉就是队列

一个简单的执行流程

public class Test {

    public static void main(String[] args) {
        new Test().test();
    }
    
    public void test(){
        
    }
}

在内存图中的结构如下

在这里插入图片描述

public class Test {

    public static void main(String[] args) {
        new Test().test();
    }
    
    public void test(){
        a()
    }
    
    public void a(){
        test();
    }
}

这样会无线堆积栈,直至栈内存溢出

在这里插入图片描述

这种错误是非常严重的,例如递归时我们经常见这样的错误,一旦发生,很难解决,避免程序OOM

对象在内存中的创建过程

public class Person {

    private Integer noid;

    private String name;

    private Integer age;

    public void info() {
        System.out.println("学生的姓名:" + name);
        System.out.println("学生的年龄:" + age);
        System.out.println("学生的noid:" + noid);
    }

    public static void main(String[] args) {
        //实例化对象
        Person person = new Person();
        //给属性赋值
        person.name = "小智";
        person.age = 20;
        person.noid = 1;
        //调用方法
        person.info();
    }
}

这段代码有3个成员变量,一个成员方法,我们为其赋值

内存结构图

在java内存中,创建对象有三个区域,栈(Stack)、堆(Heap)、方法区(Method Area)

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

最后执行方法,方法执行完毕后,方法被弹出,也叫出栈,最后main函数被弹出

在这里插入图片描述

至此,创建对象在内存中的过程完毕


Heap,一个JVM只有一个堆内存大小,堆内存的大小是可以调节的,

类加载器读取了类文件后,一般会将什么东西放到堆中?方法,全局变量,保存我们引用的真实对象

堆内存还要细分3个区域

  • 新生区(伊甸园区) Young/New
  • 养老区 old
  • 永久区 Perm

在这里插入图片描述

GC垃圾回收主要在伊甸园区和养老区

假设内存满了,就会爆OOM错误,堆内存不够

import java.util.Random;

public class Test {

    public static void main(String[] args) {
        String str = "";
        while (true) {
            str += str + new Random().nextInt(666666666)+new Random().nextInt(99999999);
        }
    }

}

在这里插入图片描述

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

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

以下三种JVM

  • HotSpot
  • BEA JRockit
  • IBM J9VM

我们使用的是HotSpot

新生区

  • 类:诞生和成长的地方,甚至死亡
  • 伊甸园区,所有的对象都是在伊甸园区new出来的
  • 幸存者区(0,1)两个来回切换 from to

老年区

老年区,经历重重GC回收还没死亡的会进入老年区,进入老年区的对象少之又少

在这里插入图片描述

真理:经过研究,有99%的对象都是临时对象,都在伊甸园区,用完即失。

永久区

这个区域是常驻内存的,用来存放JDK自身携带的Class对象,Interface存放元数据,存储的是Java运行时的一些环境或类信息,这个区域不被GC垃圾回收,在关闭虚拟机的时候就会释放这个区域的内存。

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

  • jdk1.6之前:永久代,常量池是在方法区
  • jdk1.7:永久代,满满的退化了,去永久代 常量池在堆中
  • jdk1.8之后:无永久代,元空间,常量池在元空间内

在这里插入图片描述

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

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

  • 能够看到代码第几行出错:快照分析工具,MAT:Eclipse的工具 JProfiler
  • DeBug,一行行分析

MAT、Jprofiler作用:

  • 分析Dump内存文件,快速定位内存泄漏
  • 获得堆中的错误
  • 获得大的对象~

⛵小结

以上就是【Bug 终结者】对 【高级篇】Java 进阶之JVM实战 的简单介绍,JVM 是Java 进阶必备, 在项目开发中,熟练的掌握了JVM 那就会感受到极其好的感受JVM调优,提高程序QPS,吞吐量必备,同时 也是 Java 高级面试高频,可见,掌握并灵活运用JVM就可以说是达到了高级的水平!

如果这篇【文章】有帮助到你,希望可以给【Bug 终结者】点个赞👍,创作不易,如果有对【后端技术】、【前端领域】感兴趣的小可爱,也欢迎关注❤️❤️❤️ 【Bug 终结者】❤️❤️❤️,我将会给你带来巨大的【收获与惊喜】💝💝💝!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/408809.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[遇到的问题-已解决]Cannot resolve plugin org.apache.maven.plugins:maven-compiler-plugin:3.1

如上图所示,这是我解决好的,刚开始的时候爆红有这些: 我按照在网上查找的方法,一一试了。 首先,maven 安装的路径和和本地仓库的目录必须要保持一致 打开setting-Build,Excution,Deployment-Build Tools-Maven&#…

vue框架介绍

概述 Vue 是一套用于构建用户界面的渐进式框架 对渐进式的理解: 每个框架都不可避免会有自己的一些特点,从而会对使用者有一定的要求,这些要求有强有弱,它的强势程度会影响在业务开发中的使用方式。 vue的定位: 我在做…

【Vue入门必备知识篇03】--- 生命周期 数据共享

前言❤️ 过好自律的生活,美好才会在路上不期而遇 ❤️【Vue入门必备知识篇03】--- 生命周期 & 数据共享一、生命周期 & 数据共享(1)组件的生命周期1.1 生命周期 & 生命周期函数1.2 组件生命周期函数的分类1.3 生命周期图示&…

记录Chrome插件从V2版本升级到V3版本的过程中遇到的问题

总结一下自己在把Chrome V2版本的插件升级到V3版本的过程中,遇到的一些问题,之前也有发布一章V3版本的manifest.json配置项参数说明,基本也涵盖了下面提到的几个配置项的改动,传送门>> 总结分了两大块,一块是ma…

【Node.js】深度解析常用核心模块-fs模块

✅ 作者简介:一名将要迈入大三的大学生,致力于提高前端开发能力 ✨ 个人主页:前端小白在前进的主页 🔥 系列专栏 : node.js学习专栏 ⭐️ 个人社区 : 个人交流社区 🔥前言 在文章👉Node.js — 前…

React脚手架工具创建项目的详细介绍

文章目录React脚手架工具脚手架工具解析create-react-app创建React项目目录的结构分析从零编写代码React脚手架工具 脚手架工具解析 如果我们只是开发几个小的demo程序,那么永远不需要考虑一些复杂的问题: 比如目录结构如何组织划分; 比如如何管理文件之间的相互依…

Python lxml库的安装和使用

lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 Xpath 表达式提供了良好的支持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 安装lxml库 lxml 属于 Python 第三方库,因此需要…

全网多种方式解决Unchecked runtime.lastError: The message port closed before a response was received的错误

文章目录1. 文章引言2. 分析问题3. 解决问题4. 解决该错误的其他方法1. 文章引言 今天启动项目后访问Knife4j接口文档,却报出下图错误: 在报出Knife4j文档请求异常错误时,赶紧打开控制台,如下所示: 即Unchecked runti…

CSS过渡动画

css中实现动画有两种方式:transition过渡动画、 animation自定义动画。 transition 是 css3 新增的⼀个功能,可以实现元素不同状态间的平滑过渡(当元素从⼀个状态进⼊到另⼀个状态时),经常⽤来制作⼀些动画效果。 之…

前端项目面试核心问题(持续更新)

本文有配套视频教程 项目面核心问题回答思路 说说你最近的项目 记叙文的六要素:时间、人物、地点、起因、经过、结果;时间:研发周期;人物:团队成员、分工、我负责哪几个模块;起因:项目背景、…

Vue项目实战——【基于 Vue3.x + Vant UI】实现一个多功能记账本(开发导航栏及公共部分)

基于 Vue3.x Vant UI 的多功能记账本(三) 文章目录基于 Vue3.x Vant UI 的多功能记账本(三)项目演示开发导航栏1、底部导航栏2、测试底部导航栏3、公共头部写到最后(附源码)系列内容参考链接基于 Vue3.x …

element-UI组件之日期时间选择器与时间格式转化

element-UI组件之日期时间选择器与时间格式转化日期选择器与时间选择器的一般使用日期时间选择器选择日期时间点选择日期范围日期选择器月份范围选择器禁选日期用time.getTime()进行日期(时间)格式的转换时间选择器el-time-select选择固定时间点el-time-picker选择任意时间点禁…

Vite 基本配置及原理

Vite 基本配置及原理介绍vite.config.jsoptimizeDeps.exclude不同环境的 vite 配置css配置Vite 对 css 的处理Vite 对 cssmodule 的处理和配置Vite 对预处理器的配置devSourcemapVite 对 postcss 的支持Vite 静态资源别名设置Vite 生产环境配置介绍 如果你还不知道 Vite&#…

【Vue入门必备知识篇05】--- Vue Router路由

前言❤️ 当落日余晖照在身上,没有污秽没有杂尘,只有光和希望 ❤️【Vue入门必备知识篇05】--- Vue Router路由一、前端路由的概念与原理(1)什么是路由(2)SPA 与前端路由(3)什么是前…

vue-router中的参数传递

文章目录前言一、本文章中练习需要的准备工作暴力引入bootstrap安装Vue Router二、项目基本架构1. 配置router文件2. 写入路由组件 一级路由3. 二级路由 实现传参3.1 使用params 传参3.1.1 在router文件下配置二级路由3.1.2 在view文件下新建二级路由组件3.1.3 在上一级路由组件…

uniapp**字符串转Json并提取字段值

uniapp字符串转Json并提取字段值 JSON有三种格式,每一种写法都和JS中的数据类型很像,可以很轻松的和JS中的数据类型互相转换 一、简单值的形式:JSON的简单值的格式对应着JS中的基础数据类型:数字 字符串 布尔值 注意事项&#…

这是我见过最牛逼的滑动加载前端框架

文章目录前言一、mescroll简介二、快速开始三、一分钟入门mescroll图片懒加载四、mescroll在vue中的使用五、小结前言 在手机端实现下拉刷新和下拉加载是最常见不过的需求了。今天大师兄就给大家分享一个非常精致的js框架:mescroll. 提示:以下是本篇文…

Cursor:GPT-4 驱动的强大代码编辑器

Cursor (https://www.cursor.so/)是 GPT-4 驱动的一款强大代码编辑器,可以辅助程序员进行日常的编码。下面通过一个实际的例子来展示 Cursor 如何帮助你编程。这个例子做的事情是网页抓取。抓取的目标是百度首页上的百度热搜,如下…

使用Python进行网站页面开发——HTML

目录 一、HTML基础语法 1.HTML是什么? 2.HTML基本结构 3.HTML注释 二、HTML常用标签介绍 1.文本标签 2.格式化标签 3.图片标签 4.超级链接标签 5.表格标签(用来显示数据) 6.表单标签(用来接收数据) 7.行内…

ACM模式下JavaScript(js)的输入输出 V8 Node

OJ在线编程常见输入输出练习场 ACM模式下分V8和node.js node.jsV8内置基本模块,相当于java中的JREJVMjava标准库 node就是带有能操作IO和网络库的V8引擎,提供了很多可调用的API使得JavaScript能够读写文件,网络请求,系统信息等…