Java常见问题总结五

news2024/11/16 20:26:39

1、垃圾回收方式

SerialGC(串行垃圾回收):为单线程环境设计且使用一个线程进行垃圾回收,会暂停所有的用户线程。
ParalleGC(并行垃圾回收):对过GC线程并行工作,此时用户线程是停止的。
ConcMarkSweep(CMS):用户线程和GC线程同时执行,不需要停顿用户线程。
G1垃圾回收器:将内存分割成不同的区域然后并发的进行垃圾回收。 

2、查看当前默认的垃圾回收器

java -XX:+PrintCommandLineFlags -version

3、7大垃圾回收器

在新生代主要使用:Serial copying、Parnew、Parallel Scavenge。相对应的同时会在老年代激活:Serial MSC、CMS、Parallel Compacting;以及还有一个G1垃圾回收器。

/*************************新生代*********************/
serial收集器(串行收集器):
    -- 是一个单线程的收集器,在进行垃圾回收时,暂停其他所有用户线程,有可能会产生较长的停顿状态(stop-the-world)
    -- 通过设置-XX:+UseSerialGC进行开启,会使用Serial(Young 区)+Serial old(old区)的收集器组合。
    -- 即新老代都会使用串行收集器,新生代使用复制算法,老年代使用标记-整理算法。

ParNew(并行收集器):
    -- 使用多线程进行垃圾回收,也会暂停其他所有用户线程。
    -- 通过设置-XX:+UseParNewGC启用收集器,会使用ParNew(Young区)+Serial old(old区)的收集器组合。
    -- 新生代使用复制算法,老年代采用标记-整理算法。不过现在已被移除。

Parallel New(并行收集器):
    -- 是一个吞吐量有限的收集器。串行收集器在新生代和老年代的并行化。
    -- 关注的重点是吞吐量和自适应调节策略(会根据当前系统的运行情况来提供最适应的停顿时间 -XX:+MaxGCPauseMillis)
    -- 通过设置-XX:+UseParallelGC或-XX:+UseParallelOldGC(相互激活)来使用
    -- 参数-XX:+ParallelGCThreads=N N代表启动多少个GC线程。

/*************************老年代*********************/
Parallel old
    -- 是ParallelScavenge的老年代版本
    -- 通过设置-XX:+UseParallelGC或-XX:+UseParallelOldGC(相互激活)来使用

CMS(并发标记清除)
    -- 是一种以获取最短回收停顿时间为目标的收集器。
    -- 通过设置-XX:+UseConcMarkSweepGC会自动打开-XX:+UseParNewGC。
    -- 开启后使用ParNew(Young区)+CMS(Old区)+Serial old的收集器组合。
    -- Serial old将作为CMS出错的后备收集器。CMS必须要在老年代堆内存用完之前完成回收,要不然就出发后备收集器造成较大的停顿。
    -- 一共有4步:
        1.初始标记:标记GC roots能关联到的对象,会暂停所有工作。
        2.并发标记:和用户线程一起工作,主要标记过程,标记全部对象。
        3.重新标记:暂停所有工作线程,用于确认是否又有新的标记。
        4.并发清除:清理不可达对象,和用户线程一起,不需要暂停工作线程
        5.优点是并发执行CPU资源压力大,缺点是会导致大量的碎片。

Serial old
    -- 是Serial的老年版本

以上的收集器都有这些共同点:1、新生代和老年代都是各自独立且连续的内存块。2、老年代收集必须扫描整个老年代。3、都是尽可能的少而快速的执行GC为原则。

4、G1垃圾回收器

是一种服务端的垃圾收集器,在实现高吞吐量的同时,也实现尽可能满足垃圾收集暂停时间。像CMS一样能与应用程序并发执行。

G1的优点在于:1、充分利用多核多CPU的硬件优势,尽量缩短STW(系统暂停时间)。2、整体采用标记-整理算法不会产生内存碎片。3、不再区分老年代和新生代把内存划分为多个独立的子区域。

堆有新生代(1/3)和老年代(2/3),新生代包含 伊甸园和幸存者区两个(8:1:1),当分代年龄到达一定次数就会从新生代到老年代。JVM调优的目的是减少STW(stopedWord),大对象(数组、字符串)会直接进入老年代。

回收步骤:

  1. Eden区的数据移动到Survivor区,如果Survivor区内存不够,Eden区数据部分移动到old区。
  2. Survivor区移动到新的Survivor区,部分数据回到old区。
  3. 最后Eden区收拾干净,GC结束。

G1相关的参数:

-XX:+UseG1GC 使用G1垃圾回收器
-XX:G1HeapRegionSize=n  设置G1区域的大小,值是2的幂从1MB--32MB
-XX:MaxGCPauseMillis=n  设置GC的停顿时间,JVM尽可能停顿时间少于这个时间。
-XX:ConcGCThreads=n 并发使用GC的线程数
-XX:InitialingHeapOccupancyPercent=n  设置堆占用多少就出发GC
-XX:G1ReservePrecent=n  设置空闲时间的预留内存百分比

Springboot相关JVM调优

java -server [jvm相关参数]  -jar 包名

5. Java内存对象布局

一个对象实例包含对象头、实例数据、对齐填充;对象头包含对象标记(MarkWord) 、类型指针、长度(只有数组对象有,普通对象中没有);MarkWord包含了hashcode、GC标记、GC次数、同步锁标记、偏向锁持有者。在64位系统中MarkWord占8字节、类元指针占8字节,总共16字节。

 这就是为什么GC最大年龄是15,因为二进制1111就是15,age只分配了四位。类型指针是指向类元数据的指针(也就是指向方法区的 xxx的klass);实例数据是存放类属性field的信息,包括父类信息;对齐填充虚拟机要求对象起始地址必须是8字节的整倍数,不是必须存在的,仅仅为了对齐。一个对象的大小 = MarkWord(8字节) + 类型指针(4字节) + 实例数据(根据实际情况,空对象没有) + 对其填充(根据实际情况)。

通过JOL查看对象的内存分布情况,首先先导入maven信息。

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.9</version>
</dependency>

 通过JOL就可以查看当前虚拟机信息以及对象的内存布局。

Object o = new Object();
System.out.println(VM.current().details());  // 获取当前虚拟机信息
System.out.println(VM.current().objectAlignment());   //  获取对象的对齐数
System.out.println(ClassLayout.parseInstance(o).toPrintable()); //  获取一个对象的布局

对象的内存布局:这里的前两个header是MrakWord,第三个是类型指针,第四个是填充对齐。这里默认是开启了指针压缩导致类型指针由8字节压缩成4字节。SIZE代表字节大小,OFFSET代表偏移量也就是这个字段所占的byte数,TYPE代表class中定义的类型,VALUE代表内存中的值,DESCRIPTION代表类型的描述。value值需要从右下角到左上角倒着看,每个字节按照顺序,字节之间按照逆序

 6. 锁升级的过程

在JDK1.6之前只有无锁和重量级锁两种模式,由于重锁是在内核态处理的,所以从无锁到重锁之间切换时非常消耗资源的,为了减少获得和释放锁的资源消耗,添加了两种锁的方式偏向锁和轻量级锁;在JDK15后偏向锁被废弃了。锁升级的过程如下。

无锁 --> 偏向锁 --> 轻量级锁(CAS) --> 重量级锁(synchronized)

※ 无锁

在无锁的状态下打印对象的内存布局,可以发现最后两位是01,正是无锁的状态,但是现在还没有看见hashCode的值?

 至于为什么没有看见hashCode的值是因为没有调用hashCode方法,只有手动调用后才会显示。

 ※ 偏向锁

是单线程竞争;一段同步代码一直被同一个线程访问,由于只有一个线程,那么该线程在后续访问中会自动获得锁。换言之,偏向锁会偏向第一个访问锁的线程,如果锁后续没有被其他线程访问,则持有偏向锁的线程永远不需要触发同步,也即偏向锁在资源没有竞争下消除了同步语句,都不用CAS,可直接提高性能。

可以通过命令在系统中看到,在JDK1.8中默认是开启偏向锁的,延迟4s(也就是程序执行4s后才会开启偏向锁,在JDK11中没有延迟)。

 有两种触发偏向锁的方式,一种是直接在程序执行的时候直接sleep大于4秒的时间;第二种就是通过给定Java虚拟机参数。

-XX:BiasedLockingStartupDelay=0  将延迟设置为0s
-XX:-UseBiasedLocking   关闭偏向锁

偏向锁的流程:偏向锁会将第一个访问锁的线程Id记录在自己的MarkWord中,第二次,会先判断当前线程Id和MarkWord保存的是否一致,如果一致则直接进入同步代码块无需加锁;如果不一致则需要抢占,如果抢占成功则修改MarkWord的中记录的线程Id,此时不会升级锁;如果失败则会在等待到 全局安全点(不会执行任何代码)、同时检查持有的偏向锁是否还在执行判断,如果持有偏向锁的线程正在执行同步代码块,则升级完CAS后,该线程还会占用CAS;如果持有偏向锁的线程已退出同步代码块,则剩下的线程进行CAS竞争;之后就会撤销偏向锁。

public void func2(){
    Object o = new Object();
    new Thread(() -> {
        synchronized (o){
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
        }
    }).start();
}

设置完启动参数以后我们再次打印对象的内存布局会发现,MarkWord已经加上了线程Id。

 ※ 轻量级锁

是多线程竞争;但是任意时刻最多一个线程进行竞争,即不是很激烈的情况。可以通过关闭偏向锁的命令来验证轻量级锁。

Object o = new Object();
new Thread(() -> {
    synchronized (o){
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }
}).start();

当轻量级锁自旋一定次数以后就会升级成重量级锁。自旋次数在Java6之前自旋默认10次,或者自旋线程数超过CPU核数的一半,可以通过 -XX: PreBlockSpin = 10来修改;在Java6之后采用自适应自旋锁,其原理是如果本次自旋成功了,那么下次就会增大自旋的次数,反之会减少自选次数, 减少CPU的空转。

※ 重量级锁

Object o = new Object();
new Thread(() -> {
    synchronized (o){
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }
}).start();
new Thread(() -> {
    synchronized (o){
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }
}).start();

 升级成重量级锁之后 hashCode和GC年龄都去哪里了??

当一个对象计算过hashCode就无法进入偏向锁的状态了,会跳过偏向锁阶段直接进入轻量级锁;如果正处于偏向锁,并在此时调用了hashCode,就会撤销偏向锁,升级成为重量级锁(如果可以的话那么前后两次的hashCode就不能保持唯一);如果处于轻量锁状态,hashCOde会在当前锁的栈帧中存放,释放锁会将信息重新写回对象头内;如果处于重量级锁,对象头指向了重量级锁的位置 ObjetMonitor对象中会存储hashCode。

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

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

相关文章

Tatuk GIS Developer Kernel for .NET 最新Crack

Tatuk GIS Developer Kernel for .NET 是一个地理SDK&#xff0c;它是受控代码和 .NET GIS SDK&#xff0c;用于为用户 Windows 操作系统创建 GIS 专业软件的过程。它被认为是一个完全用于 Win Forms 的 .NET CIL&#xff0c;WPF 的框架是为 C# 以及 VB.NET、VC、oxygen 以及最…

StarRocks携手零洞科技,助力碧桂园物业企业微信数字化项目

作者&#xff1a;零洞科技大数据部零洞科技有限公司&#xff08;以下简称“零洞”&#xff09;&#xff0c;是碧桂园集团的核心联盟企业&#xff0c;致力于成为国内领先的数智空间解决方案服务商&#xff0c;业务场景覆盖户内及户外&#xff0c;在智慧家居板块&#xff0c;打造…

【C语言】字符分类函数+内存函数

目录 1.字符函数 1.1字符分类函数 1.2.字符转换函数 //统一字符串中的大小写 2.内存处理函数 2.1内存拷贝函数memcpy //模拟实现memcpy 2.2内存移动函数memmove //模拟实现memmove 2.3内存比较函数memcmp 2.4内存设置函数memset 1.字符函数 1.1字符分类函数 头文…

【微信小程序】-- 宿主环境 通信模型 运行机制介绍(五)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#…

NFTScan x TiDB丨一栈式 HTAP 数据库为 Web3 数据服务提供毫秒级多维查询

导读 NFTScan 是一家多链 NFT 数据基础设施服务商&#xff0c;为 Web3 用户提供高效简洁的 NFT 资产搜索查询服务&#xff0c;为 Web3 开发者和新一代金融科技公司提供专业的 NFT API 数据服务。 TiDB 作为一种分布式 HTAP 数据库&#xff0c;可以同时满足海量数据存储和高并…

I2C实验

目录 一、I2C简介 二、硬件原理 1、看原理图&#xff0c;找到I2C 2、查看使用设备 3、查看使用的IO 4、查数据手册看复用位 三、查看寄存器 1、I2C Address Register (I2Cx_IADR) 2、I2C Frequency Divider Register (I2Cx_IFDR) 3、I2C Control Register (I2Cx_I2CR) …

【C++】哈希表

1. unordered系列关联式容器 在C98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时效率可达到 &#xff0c;即最差情况下需要比较红黑树的高度次&#xff0c;当树中的节点非常多时&#xff0c;查询效率也不理想。最好的查询是&#xff0c;进行…

TCP/IP网络协议介绍及原理分析

一.应用层协议对于应用层而言&#xff0c;协议是开发者自己进行定义的&#xff0c;开发者根据自定义的格式规范对数据进行编码和解析。但是从原理上进行分析&#xff0c;其核心主要包括两点内容&#xff1a;①确定客户端和服务端交互的内容&#xff08;协议的内容&#xff09;②…

记一次docker虚拟机横向移动渗透测试

本次渗透在几个docker虚拟机间多次横向移动&#xff0c;最终找到了一个可以进行docker逃逸的出口&#xff0c;拿下服务器。渗透过程曲折但充满了乐趣&#xff0c;入口是172.17.0.6的docker虚拟机&#xff0c;然后一路横向移动&#xff0c;最终在172.17.0.2出实现了docker逃逸&a…

【免费教程】地下水环境监测技术规范HJ/T164-2020解读使用教程

地下水环境监测技术规范依据《中华人民共和国环境保护法》第十一条“国务院环境保护行政主管部门建立监测制度、制订监测规范”和《中华人民共和国水污染防治法》的要求&#xff0c;积极开展地下水环境监测&#xff0c;掌握地下水环境质量&#xff0c;保护地下水水质&#xff0…

常青科技冲刺A股上市:研发费用率较低,关联方曾拆出资金达1亿元

近日&#xff0c;江苏常青树新材料科技股份有限公司&#xff08;下称“常青科技”或“常青树科技”&#xff09;递交招股书&#xff0c;准备在上海证券交易所主板上市。本次冲刺上市&#xff0c;常青科技计划募资8.50亿元&#xff0c;光大证券为其保荐机构。 据招股书介绍&…

我的 System Verilog 学习记录(4)

引言 本文简单介绍 System Verilog 语言的 数据类型。 前文链接&#xff1a; 我的 System Verilog 学习记录&#xff08;1&#xff09; 我的 System Verilog 学习记录&#xff08;2&#xff09; 我的 System Verilog 学习记录&#xff08;3&#xff09; 数据类型简介 Sys…

Linux:共享内存api使用

代码&#xff1a; #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <arpa/inet.h> #include <sys/un.h> #include <sys/ipc.h…

Codeforces Round #849 (Div. 4)(E~G)

A~D比较简单就不写了&#xff0c;哎嘿E. Negatives and Positives给出一个数组a&#xff0c;可以对数组进行若干次操作&#xff0c;每次操作可以将相邻的两个数换为它们的相反数&#xff0c;求进行若干次操作之后能得到数组和的最大值是多少。思路&#xff1a;最大的肯定是把负…

VSCode+Qt+MinGW开发环境搭建

VSCodeQtMinGW开发环境搭建 概述 VSCode扩展性很强&#xff0c;插件机制让其具备不断演进的潜力&#xff0c;适合作为稳定的开发工具。 VSCodeQt开发环境的搭建需要依赖于以下工具&#xff1a; VSCode、Qt&#xff0c;其中Qt需要安装MinGW编译工具&#xff1b;VSCode插件&a…

常年霸榜TK彩妆类目,看Focallure菲鹿儿如何玩转出海市场

据市场调研机构弗若斯特沙利文数据报告&#xff0c;2017年至2021年&#xff0c;中国化妆品市场规模由6305亿元增长至9468亿元&#xff0c;年均复合增长率为10.7%&#xff0c;报告预计2023年中国化妆品市场规模将达11414亿元&#xff0c;今后几年的增速将逐渐放缓。随着国内市场…

LeetCode刷题系列 -- 112. 路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。叶子节点 是指没有子节点的…

外置的媒体查询,对性能又一次的优化提升

通常情况下我们写媒体查询都是写在一个样式文件中&#xff0c;对于浏览器加载的时候&#xff0c;会解析到最后一行样式时才会渲染页面&#xff0c;这样就会造成页面的白屏时间过长。 但是通常情况下大量的媒体查询样式都是无用的&#xff0c;现在浏览器允许我们在引用样式文件…

SpringBoot优雅地处理全局异常,返回前端

笔者这边提供了两种处理全局异常的方式。这两种方式各有千秋&#xff0c;都很优雅。至于伙伴们想用哪种方式&#xff0c;那就仁者见仁&#xff0c;智者见智了。0、公共部分在介绍异常处理方式前&#xff0c;先定义一些公共的类。这些类在两种处理方式中都会用到。【自定义业务异…

jupyter的安装步骤

1.安装python文件 首先去官网python去下载python的安装包&#xff0c;点击donwload,选择合适的系统。这里我是windown系统&#xff0c;点击进去&#xff0c;如图找到有installer的去下载。不建议下载最新版本的&#xff0c;会有兼容问题。 2.安装python 点击第二个选项是自己配…