【JVM基础】JVM入门基础
目录
- JVM的位置
- 三种 JVM
- JVM体系结构
- 类加载器
- 双亲委派机制
- 概念
- 例子
- 作用
- 沙箱安全机制
- 组成沙箱的基本组件
- Native
- JNI:Java Native Interface(本地方法接口)
- Native Method Stack(本地方法栈)
- PC寄存器(Program Counter Register)
- 方法区(Method Area)
- 栈(Java Stack)
- 栈 + 堆 + 方法区:交互关系
- 堆(Heap)
- 新生区 (伊甸园+幸存者区*2)
- 老年区
- 永久区
- 堆内存调优
- 报OOM怎么办?
- GC(垃圾回收)
- 引用计数法
- 复制算法
- 标记清除
- 标记压缩(标记整理):再优化
- 标记清除压缩:再优化
- 分代收集算法
- 总结
JVM的位置
应用程序(Java应用程序)在JRE上运行(JRE包含JVM),JRE在操作系统(Windows、Mac)上运行,操作系统在硬件体系(Intel、Spac…)上运行。
三种 JVM
- Sun公司:HotSpot 用的最多(我们使用)
- BEA:JRockit
- IBM:J9VM
JVM体系结构
JVM 调优:99%都是在方法区和堆,大部分时间调堆。 JNI(Java Native Interface):本地方法接口


类加载器
作用:加载class文件
例如:new Student();(具体实例在堆里,引用变量名放栈里)
- 虚拟机自带的加载器
- 启动类(根)加载器
- 扩展类加载器
- 应用程序加载器


双亲委派机制
概念
当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。
例子
当一个 Hello.class 这样的文件要被加载时。
不考虑我们自定义类加载器,首先会在 AppClassLoader 中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的 loadClass 方法。
父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达 Bootstrap classLoader 之前,都是在检查是否加载过,并不会选择自己去加载。
直到 BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。

作用
- 1、防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
- 2、保证核心.class不能被篡改。通过委托方式,不会去篡改核心.class,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。
比如:如果有人想替换系统级别的类:String.java。
篡改它的实现,在这种机制下这些系统的类已经被 Bootstrap classLoader 加载过了(为什么?因为当一个类需要加载的时候,最先去尝试加载的就是 BootstrapClassLoader ),所以其他类加载器并没有机会再去加载,从一定程度上防止了危险代码的植入。
沙箱安全机制




组成沙箱的基本组件
- 字节码校验器(bytecode verifier)
确保 Java 类文件 .Class 遵循 Java 语言规范。这样可以帮助 Java 程序实现内存保护。但并不是所有的类文件都会经过字节码校验,比如核心类。 - 类装载器(class loader)
其中类装载器在3个方面对 Java 沙箱起作用:- 它防止恶意代码去干涉善意的代码; //双亲委派模式
- 它守护了被信任的类库边界;
- 它将代码归入保护域,确定了代码可以进行哪些操作。
虚拟机为不同的类加载器载入的类提供不同的命名空间,命名空间由一系列唯一的名称组成,每一个被装载的类将有一个名字,这个命名空间是由 Java 虚拟机为每一个类装载器维护的,它们互相之间甚至不可见。
类装载器采用的机制是双亲委派模式。
1、从最内层 JVM 自带类加载器开始加载,外层恶意同名类得不到加载从而无法使用;
2、由于严格通过包来区分了访问域,外层恶意的类通过内置代码也无法获得权限访问到内层类,破坏代码就自然无法生效。
- 存取控制器(access controller):存取控制器可以控制核心 API 对操作系统的存取权限,而这个控制的策略设定,可以由用户指定。
- 安全管理器(security manager):是核心 API 和操作系统之间的主要接口。实现权限控制,比存取控制器优先级高。
- 安全软件包(security package):java.security 下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性,包括:
- 安全提供者
- 消息摘要
- 数字签名 keytools https(需要证书)
- 加密
- 鉴别
Native
凡是带了 native 关键字的,说明 Java 的作用范围达不到了,得回去调用底层C语言的库
凡是带了 native 关键字的方法会进入本地方法栈,其它的是 Java栈
JNI:Java Native Interface(本地方法接口)
调用本地方法接口(JNI)作用:
扩展 Java 的使用,融合不同的编程语言为 Java 所用
Java 诞生的初衷是融合C/C++程序,C、C++横行,想要立足,必须要有调用C、C++的程序,它在内存区城中专门开辟了块标记区城: Native Method Stack
Native Method Stack(本地方法栈)
登记 native 方法,在执行引擎(Execution Engine)执行的时候。通过JNI (本地方法接口)加载**本地方法库(Native Libraies)**中的方法。
在企业级应用中少见,与硬件有关应用:Java程序驱动打印机,系统管理生产设备等
PC寄存器(Program Counter Register)
程序计数器: Program Counter Register
每个线程都有一个程序计数器,是线程私有的,就是一个指针, 指向方法区中的方法字节码 ( 用来存储指向下一条指令的地址, 也即将要执行的指令代码 ), 在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。
方法区(Method Area)
方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说:所有定义的方法的信息都保存在该区域,此区域属于共享区间;
静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池(如:static,final,,Class(类模板), 常量池)存在方法区中,但是实例变量存在堆内存中,和方法区无关。
栈(Java Stack)
为什么 main() 先执行,最后结束:(因为一开始 main() 先压入栈)
栈:栈内存,主管程序的运行,生命周期和线程同步。
线程结束,栈内存也就释放,对于栈来说,不存在垃圾回收问题。
栈存放:8大基本类型+对象引用+实例的方法。
栈运行原理:栈帧(局部变量表+操作数栈)每调用一个方法都有一个栈帧。
栈满了 main() 无法结束,会抛出错误:栈溢出 StackOverflowError

栈 + 堆 + 方法区:交互关系

堆(Heap)
一个 JVM 只有一个堆内存,堆的大小是可以调节的。
类加载器读取了类文件后,一般会把 类,方法,常量,变量,保存所有引用类型的真实对象放到堆中。
堆内存细分3个区域:
- 新生区(伊甸园区) Young / new
- 养老区 old
- 永久区 Perm ,在JDK8以后,永久存储区改了个名字 (元空间)
GC 垃圾回收,主要是在 伊甸园区 和 养老区。

假设内存满了,报错 OOM:堆内存不够 OutOfMemoryError:Java heap space
//-Xms8m -Xmx8m -XX:+PrintGCDetails
public static void main(String[] args) {String str = "javajavajavajava";while (true){str += str + new Random().nextInt(888888888)+ new Random().nextInt(21_0000_0000);}
}
//OutOfMemoryError:Java heap space 堆内存满了

新生区 (伊甸园+幸存者区*2)
- 类诞生和成长甚至死亡的地方
- 伊甸园,所有对象都是在伊甸园区 new 出来的
- 幸存者区(from, to),轻GC定期清理伊甸园,活下来的放入幸存者区,幸存者区满了之后重GC 清理伊甸园+幸存者区,活下来的放入养老区。都满了就报 OOM。
注:经过研究,99%的对象都是临时对象!直接被清理了
老年区
新生区剩下来的,轻GC杀不死了
永久区
这个区域常驻内存,用来存放 JDK 自身携带的 Class 对象,Interface 元数据,存储的是 Java 运行时的一些环境或类信息,该区域不存在垃圾回收GC。关闭虚拟机就会释放这个内存。
- jdk1.6之前:永久代,常量池在方法区。
- jdk1.7:永久代,但是慢慢退化了(去永久代)常量池在堆中。
- jdk1.8之后:无永久代,常量池在元空间。
常量池一直在方法区,其中的字符串池 JDK1.7之后保存到了堆中。
永久区 OOM 例子:一个启动类,加载了大量的第三方jar包。Tomcat 部署了太多的应用,大量动态生成的反射类。不断的被加载。直到内存满,就会出现 OOM。
方法区又称非堆 (non-heap),本质还是堆,只是为了区分概念。
元空间逻辑上存在,物理上并不存在。
堆内存调优
public static void main(String[] args) {//返回虚拟机试图使用的最大内存long max = Runtime.getRuntime().maxMemory(); //字节 1024*1024//返回jvm初始化的总内存long total = Runtime.getRuntime().totalMemory();System.out.println("max="+max+"字节\t"+(max/(double)1024/1024+"MB"));System.out.println("total="+total+"字节\t"+(total/(double)1024/1024+"MB"));/* 运行后:max=1866465280字节 1780.0MBtotal=126877696字节 121.0MB*///默认情况下,分配的总内存占电脑内存1/4 初始化1/64
}
报OOM怎么办?
-
1.尝试扩大堆内存,如果还报错,说明有死循环代码 或垃圾代码
Edit Configration>add VM option> 输入:-Xms1024m -Xmx1024m -XX:+PrintGCDetails

新生区+养老区:305664K+699392K=1005056K = 981.5M ,说明元空间物理并不存在。 -
2.分析内存,看一下哪个地方有问题(专业工具)
能够看到代码第几行出错:内存快照分析工具,MAT,Jprofiler
MAT,Jprofiler作用:- 分析Dump内存文件,快速定位内存泄漏;
- 获得堆中的数据
- 获得大的对象
//-Xms 设置初始化内存分配大小 默认1/64
//-Xmx 设置最大分配内存,默认1/4
//-XX:+PrintGCDetails 打印GC垃圾回收信息
//-XX:+HeapDumpOnOutOfMemoryError //oom DUMP
//-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
public class Demo03 {byte[] array = new byte[1*1024*1024]; //1mpublic static void main(String[] args) {ArrayList<Demo03> list = new ArrayList<>();int count = 0;try {while (true){list.add(new Demo03()); //不停地把创建对象放进列表count = count + 1;}} catch (Exception e) {System.out.println("count: "+count);e.printStackTrace();}}
}


GC(垃圾回收)

JVM在进行GC时,并不是对新生代、幸存区、老年区,这三个区域统一回收。大部分时候回收的是新生代
GC两种:轻GC,重GC (Full GC,全局GC)
引用计数法
一般 JVM 不用,大型项目对象太多了

复制算法
-XX:MaxTenuringThreshold=15 设置进入老年代的存活次数条件。


好处:没有内存的碎片,内存效率高
坏处:浪费了内存空间(一个幸存区永远是空的);假设对象100%存活,复制成本很高。
复制算法最佳使用场景:对象存活度较低的时候,新生区。
标记清除

优点:不需要额外空间,优化了复制算法。
缺点:两次扫描,严重浪费时间,会产生内存碎片。
标记压缩(标记整理):再优化
三部曲:标记–清除–压缩

标记清除压缩:再优化
每标记清除几次就压缩一次,或者内存碎片积累到一定程度就压缩。
分代收集算法
根据内存对象的存活周期不同,将内存划分成几块,JVM一般将内存分成新生代和老生代。
在新生代中,有大量对象死去和少量对象存活,所以采用复制算法,只需要付出少量存活对象的复制成本就可以完成收集;
老年代中因为对象的存活率极高,没有额外的空间对他进行分配担保,所以采用标记清理或者标记整理算法进行回收;

总结
内存效率:复制算法 > 标记清除算法 > 标记压缩算法(时间复杂度)
内存整齐度:复制算法 = 标记压缩算法 > 标记清除算法
内存利用率:标记压缩算法 = 标记清除算法 > 复制算法
没有最好的算法,只有合适的算法(GC也被称为分代收集算法)。
- 年轻代:存活率低,用复制算法。
- 老年代:存活率高,区域大,用标记-清除-压缩。
相关文章:
【JVM基础】JVM入门基础
目录 JVM的位置三种 JVMJVM体系结构类加载器双亲委派机制概念例子作用 沙箱安全机制组成沙箱的基本组件 NativeJNI:Java Native Interface(本地方法接口)Native Method Stack(本地方法栈) PC寄存器(Program…...
【SpringBoot】详细介绍Spring Boot中@Component
在Spring Boot中,Component是一个通用的注解,用于标识一个类是Spring框架中的组件。Component注解是Spring的核心注解之一,它提供了自动扫描和实例化bean的功能。 具体来说,Component注解的作用是将一个普通的Java类转化为Spring…...
Redis执行lua脚本-Time函数-获取当前时间
演变过程: TIME 命令返回当前服务器的时间,包含两个条目 Unix 时间戳和这一秒已经过去的微秒数。 eval " local res redis.call(time); return res; " 0 eval " local current_time redis.call(TIME) local unix_timestamp tonumb…...
前端无需install快速调试npm包,Console-Import使用
Console-Import是一个Chrome扩展插件,可以方便地从Chrome控制台导入JavaScript和CSS资源。它可以帮助我们在开发过程中快速调试和测试第三方库或代码。 下载地址 安装 要安装Console-Import,请在Chrome网上应用店搜索“Console-Import”,然…...
构建稳定的爬虫系统:如何选择合适的HTTP代理服务商
在构建一个稳定、高效的爬虫系统中,选择合适的HTTP代理服务商是至关重要的一步。本文将介绍如何选取可靠且性能优秀的HTTP代理服务供应商,来完成搭建一个强大而稳定的爬虫系统。 1.了解不同类型和特点 -免费公开代理服务器:提供免费但可能存在限制或不…...
Python爬虫基础:使用Scrapy库初步探索
Scrapy是Python中最流行的网页爬虫框架之一,强大且功能丰富。通过Scrapy,你可以快速创建一个爬虫,高效地抓取和处理网络数据。在这篇文章中,我们将介绍如何使用Scrapy构建一个基础的爬虫。 一、Scrapy简介及安装 Scrapy是一个用…...
MacBookPro重装系统图文教程
关机 长按电源按钮10s即可强制关机 快捷键选择 Intel Command-R:获得安装过的最新的 macOS,但不会升级到最高版Option-Command-R:获得与 Mac 兼容的最新版 macOSShift-Option-Command-R:获得 Mac 自带的 macOS 或者与它最接近且…...
Android 6.0长按电源键添加重启菜单
重启图标:frameworks/base/core/res/res/drawable-hdpi/ic_lock_power_reboot_alpha.pngframeworks/base/core/res/res/drawable/ic_lock_power_reboot.xml <?xml version"1.0" encoding"utf-8"?> <!-- Copyright (C) 2014 The And…...
Python股票交易---均值回归
免责声明:本文提供的信息仅用于教育目的,不应被视为专业投资建议。在做出投资决策时进行自己的研究并谨慎行事非常重要。投资涉及风险,您做出的任何投资决定完全由您自己负责。 在本文中,您将了解什么是均值回归交易算法ÿ…...
机器人制作开源方案 | 桌面级机械臂--本体说明+驱动及控制
一、本体说明 1. 机械臂整体描述 该桌面级机械臂为模块化设计,包含主机模块1个、转台模块1个、二级摆动模块1个、可编程示教盒1个、2种末端执行器、高清摄像头,以及适配器、组装工具、备用零件等。可将模块快速组合为一个带被动关节的串联3自由度机械臂…...
有哪些前端调试和测试工具? - 易智编译EaseEditing
前端开发调试和测试工具帮助开发人员在开发过程中发现和修复问题,确保网站或应用的稳定性和性能。以下是一些常用的前端调试和测试工具: 调试工具: 浏览器开发者工具: 现代浏览器(如Chrome、Firefox、Safari等&#…...
【数据结构】手撕单链表
目录 一,链表的概念及结构 二,接口实现 1,单链表的创建 2,接口函数 3,动态创立新结点 4,打印 5,头插 6,头删 7,尾插 8,尾删 9,查找 10ÿ…...
两个git本地如何配置两个ssh密钥for mac
我是在mac上操作的。windows上也差不多一样操作。 1.找到本地的.ssh文件。我的文件结构如下如: 文件结构: (1)两个known_hosts文件是自动生成的,不用管 (2)readme文件是我个人记事本记录笔记…...
iOS逆向进阶:iOS进程间通信方案深入探究与local socket介绍
在移动应用开发中,进程间通信(Inter-Process Communication,IPC)是一项至关重要的技术,用于不同应用之间的协作和数据共享。在iOS生态系统中,进程和线程是基本的概念,而进程间通信方案则为应用的…...
qt day 1
this->setWindowIcon(QIcon("D:\\zhuomian\\wodepeizhenshi.png"));//設置窗口的iconthis->setWindowTitle("鵬哥快聊");//更改名字this->setFixedSize(500,400);//設置尺寸QLabel *qlnew QLabel(this);//創建一個標簽ql->resize(QSize(500,20…...
针对java中list.parallelStream()的多线程数据安全问题我们采用什么方法最好呢?
当使用List.parallelStream()方法进行多线程处理时,可能会涉及到数据安全问题。下面是一些常见的方法来处理parallelStream()的多线程数据安全问题: 1. 使用线程安全的集合:Java中提供了线程安全的集合类,如CopyOnWriteArrayList…...
校园用电安全管理系统可以识别违规电器吗
校园用电安全管理系统是处理恶意用电问题有效手段之一,系统具有实时监测、异常预警、监测设备运行状态、远程控制用电等功能,可以从根本上管理学校用电量,制定合理的用电计划,限制用电成本,避免各种恶意用电行为&#…...
前端(十五)——开源一个用react封装的图片预览组件
👵博主:小猫娃来啦 👵文章核心:开源一个react封装的图片预览组件 文章目录 组件开源代码下载地址运行效果展示实现思路使用思路和api实现的功能数据和入口部分代码展示 组件开源代码下载地址 Gitee:点此跳转下载 CSDN…...
idea新建Java-maven项目时,出现Dependency ‘ xxx(jar包名)‘ not found的解决方案
项目场景: 项目场景:使用idea创建maven项目时,导入简单依赖时(本文以mysql-connector-java为例)。 问题描述 问题: 首先,在创建新的maven项目中,出现下列两种情况: &am…...
C# 获取Windows系统版本注意事项
首先通过微软官方文档:https://learn.microsoft.com/zh-cn/windows/win32/sysinfo/operating-system-version了解各个操作系统对应的版本号 下面介绍3种获取版本号的方式及弊端 1. Environment.OSVersion.Version OperatingSystem os Environment.OSVersion;// 判断…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
