整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

JVM 性能调优之通过 JProfile 和 JFR

JVM 性能调优之通过 JProfile 和 JFR 分析系统瓶颈提升系统性能

成 jfr

JDK飞行记录器(JFR)是一种结构化日志记录工具, 它记录广泛的系统级(system-level)事件。类似于飞机上的黑盒子,它会持续记录飞行数据,用于调查飞行事故。JFR会持续记录JVM中的 一系列事件,用于诊断问题。这种方式的优势是,它会按时间顺序,捕获导致事故的,详细系统信息。JFR被设计的,对于性能影响很小,所以 可以安全地在生产环境长时间运行。

优势:

  1. 对于运行系统的影响比较小,额外占用资源小于 1%
  2. 生成的文件比较小,通常生成 10 多分钟的文件往往小于 1G

查询 java 进程

  1. 通过 jps 命令查询
image.png
  1. 通过 ps -ef | grep java 查询

生成 jfr 文件

生成飞行记录,使用 JFR.start 该实用程序的诊断命令 jcmd。

jcmd 84743 JFR.start duration=5m settings=profile filename=~/jfr/xxkk.jfr


JProfile 介绍

JProfiler是一个用于分析运行JVM内部情况的专业工具。在开发中你可以使用它,用于质量保证,也可以解决你的生产系统遇到的问题。
JProfiler处理四个主要问题:

  • 方法调用这通常被称为"CPU分析"。方法调用可以通过不同的方式进行测量和可视化, 分析方法调用可以帮助了解你的应用程序正在做什么,并找到提高其性能的方法。
  • 分配分析堆上对象的分配、引用链和垃圾回收属于"内存分析"的范畴。这个功能可以让你解决内存泄漏,总之使用更少的内存,分配更少的临时对象。
  • 线程和锁线程可以持有锁,例如通过在一个对象上做同步。当多个线程协作时,可能会出现死锁,JProfiler可以为你可视化这种情况。此外,锁可能被争用,这意味着线程在获得锁之前必须等待。通过JProfiler可以深入了解线程及其各种锁情形。
  • 高层子系统许多性能问题发生在更高的语义层面。例如,对于JDBC调用,你可能想找出哪条SQL语句是最慢的。对于这样的子系统,JProfiler提供了"探针",将特定有效载荷附加到调用树。

JProfiler的UI是一个桌面应用程序。你可以以交互的方式实时分析JVM,也可以在不使用UI的情况下,自动化分析。保存在快照中的分析数据,可以通过JProfiler UI打开。此外,命令行工具和构建工具集成可以帮助你自动分析会话。

注意:JProfile 是商业软件,希望大家在使用的过程中购买正版授权

内存分析

记录的对象

内存分析中,可以通过记录的对象找到最耗费内存的对象。只有总对象大小超过固定阈值(通常是堆的1%)的类才会被记录。默认情况下,JFR中禁用了这个功能,因为它会引入大量的开销。
下面两个字段的解释:

  • **实例计数示例, **可以看到某个用例的堆上还剩下哪些对象(实际上会小于总大小除以每个对象的平均大小)。
  • 预估总大小,这个是一个预估值,预估在开始 jfr 记录,到 jfr 记录结束这个类的实例总大小


https://docs.oracle.com/en/java/javase/17/docs/specs/man/jfr.html

分配热点

分配热点视图与分配调用树一起,允许你直接关注负责创建所选类的方法。就像记录的对象视图,分配热点视图也支持标记当前状态和观察一段时间内的差值。视图中会添加一个差值列,它显示了热点自当_标记当前值_操作被调用后的变化。因为默认情况下,分配视图不会定期更新,所以你必须单击_计算_工具栏按钮以获得一个新数据集然后与基线值比较。

  1. 计算热点
image.png
  1. 热点分析
image.png
  1. 热点的分配类
image.png

找到主要是因为 byte[] 数组分配,成为一个分配热点方法。

测试代码

/**
* VM ARG : -Xms64m -Xmx64m
*/

public static void main(String[] args) throws InterruptedException {
List<byte[]> list=new ArrayList<>(1000);
//2kb * 10 * 120=2400kb
//数量=10 * 120 / 10=120
for (int i=0; i < 10000; i++) {
Thread.sleep(100);
byte[] arr=new byte[1024 * 2];
list.add(arr);
}
}


CPU 分析

调用树

跟踪所有的方法调用及其调用栈会消耗相当大的内存,短时间内就会耗尽所有内存。另外,在一个繁忙的JVM中,很难直观获得方法调用的数量。通常情况下,这个数字是如此之大,以至于定位和跟随跟踪是不可能的。
另一个方面,只有将收集到的数据进行汇总,许多性能问题才会变得清晰。这样,你就可以知道在某个时间段内,方法调用相对于整个活动的重要性。如果是单一的跟踪,你对你所看的数据的相对重要性没有概念。
这就是为什么JProfiler建立了一个所有观察到的调用堆栈的累积树,并注解有观察到的时间和调用次数。时间顺序信息被消除,只保留总数。树中的每个节点代表一个至少被观察过一次的调用堆栈。节点的子节点代表在该调用堆栈中看到的所有传出调用。

调用树是"CPU视图"部分的第一个视图,当你开始进行CPU分析时,它是一个很好的起点, 因为遵循方法调用从起点到最细化的终点的自上而下视图,最容易理解。JProfiler按照子节点的总时间进行排序,所以你可以深度优先打开树,分析对性能影响最大的部分。

热点

如果你的应用程序运行得太慢,你要找到那些占用大部分时间的方法。通过调用树,有时可以直接找到这些方法, 但通常这样做是行不通的,因为调用树可能很大而且有大量叶节点
在这种情况下,你需要反转调用树:一个所有方法的列表,按其总的自身时间排序,从所有不同的调用堆栈中累计出来, 并通过回溯跟踪显示这些方法是如何被调用的。在热点树中,叶节点是入口点, 就像应用程序的main 方法或线程的run 方法。从热点树中最深的节点开始,调用向上传递到顶层节点。


回溯跟踪中的调用次数和执行时间并不是指该方法节点,而是指顶层热点节点在这条路径上被调用的次数。理解这一点很重要:粗略一看,你会认为看到的节点上的信息是该节点的调用次数。然而,在热点树中,该信息显示的是该节点对顶层节点的贡献。所以,你必须这样理解这些数字: 沿着这个倒置的调用堆栈,顶层热点被调用了n 次,总持续时间为t 秒。

通过这里可以看到这个 CASE 是因为正则导致的 CPU 热点

测试代码

static String pattern=" ^([\u4e00-\u9fa5]+)((·[\u4e00-\u9fa5]+)+|([\u4e00-\u9fa5]+))$";
static String defaultName="张三·无论其是看都看呐阿斯顿啊·萨肯萨肯打开你发都看啊看你发个卡看那可能发看那个可能看呐";

public static void main(String[] args) throws InterruptedException {
int time=1;
String result;
while (true) {
if (time > 0) {
Thread.sleep(time);
}
result=defaultName.matches(pattern) ? "姓名合法" : "姓名不合法";
}
}


参考文档

  • jcmd 指令详解:https://docs.oracle.com/javacomponents/jmc-5-5/jfr-command-reference/diagnostic-command-reference.htm
  • 生成 jfr 文件:https://docs.oracle.com/javacomponents/jmc-5-5/jfr-runtime-guide/run.htm
  • jprofile 中文手册:https://www.ej-technologies.com/resources/jprofiler/v/13.0/help_zh_CN/doc/main/memory.html

从学了Python了之后,不停的在练习,今天就拿百度练下手爬取实时热点,需要用的库有requests_html 非常的简单! 程序运行结果如下图:

这里要简单说下:用到了css选择器,选择list-title的元素对象,还有需要注意的是这里百度的页面需要编码,因为不是UTF-8的! r.encoding=r.apparent_encoding 用这个即可解决! 获取的链接是一个集合,我们只需要for 遍历输出即可!

下面附上源代码,如有不足的地方欢迎大神指出!学无止境,让我们一起学习Python吧!

tml 的结构部分标签



二、网页常见的结构


header 标签 --- 头部分结构

nav 标签 --- 导航条部分结构

main 标签 --- 主核心部分内容

footer 标签 --- 页尾


01.代码示例:

<body>


<header>...</header>

<nav>...</nav>

<main>...</main>

<footer>...</footer>


</body>


02.以上源代码使用 DOM(文件对象模型)表示:


body

header

nav

main

footer


03. header 与 footer 用法


header --- 指整个网页/或某个区块的头部。

一定会包含该区块的标题

可能会包含该区块的副标题、版本、署名等等


footer --- 表示整个网页/或某个区块的脚部

可能包括版权信息、参考资源、附注等等


小结:

头是header,脚是footer。

header 和 footer 都可能多次出现,但它们的级别是不同的。


04.nav(navgation 导航) --- 包含一组链接,连接至网站其它主要页面。


nav 标签 与 header 标签的关系

不建议将nav放置在header内


头部/header是从上下文逻辑关系上来说的,并不一定是视觉设计中摆在顶上的内


容,所以一般不会包含nav。


小结:

导航 navigation包含一组链接,指向网站的其他主要栏目。

nav和header通常是互相独立的并列关系。

nav可能在页面中多次出现,分别指主导航、二级导航等等。


05.main 标签和 aside 标签


main --- 代表网页中的主要内容/主结构

不会包含网站的主导航条、网站总标题和网站总页脚,但可能包含次一级别的


header/van/footer。


*注:每个网页最多只有一个main


main 不可能是以下元素的后代:


article(文章标签)、aside(附属内容标签)、footer(网页标签)、deader(头部标签) 或


nav(导航标签)


aside(附属内容)

附属内容

如果删除,影响不大

应与主内容main相关

aside有时被翻译成"侧边",这种翻译是不准确的。


代码示例:

<body>


<header>...</header>

<nav>...</nav>

<main>

</main>

<aside></aside>

<footer>...</footer>

</body>


按 DOM(文件对象模型)构建 html :


body

header

nav

main

aside

footer


小结:

01.主内容main,每个页面只有一个,指该页面专属的内容块,区别于网站的其他页面。


02.附属内容aside,与主内容有一些联系,如果被删除,对网页的阅读没有明显影响。


06. article 标签与 section 标签


article 是一篇完整的文章,代表页面中独立的、完整的、可以独自被外部引用的内容。


注意:header/footer/nav都可能出现在article中,作为文章自身的头、尾与文章内部导航。


section (片段或章节) :


无法独立成文,通常性属于 article 的一部分,该片段里的开始部分仍有一个自然标题以


及相关内容。


section 也可能是main的子元素。


07.关于 id :

唯一标识符,同一页面里id不得重复,以半角英文字母开头,不含特殊字符与空格,描述职能


,不描述样式。


小结:

01.article 是一篇独立的完整的文章,可以被外界引用的。


02.section 可以是 article 中的一个章节,也可以是主内容中的一个区块。


03.不管是 article 还是 section , 第一个子元素总是标题(header 或 h1)。


08. div 标签(部门、分块)


div --- 根据职能的不同,划分出特定的一块区域,常常结合id属性使用。


示例:

<header>

<h1>internet...</h1>

</header>

<div id="today">...</div> ---今日热点功能区域

<div id="login">...</div> ---登陆相关功能区域

<div id="search">...</div> ---搜索功能区域


article、section、div 的区别:


aricle:文章

如果上下文能联合组成一个有意义的独立文章;应优先使用 article


section:片段,章节

无法使用article时,该片段里的开始部分仍有一个自然标题,和相关内容。


div:部门、分块

在无法使用article/section/aside等时,仍承担着特定功能的分块。


小结:

在无法使用article/section/aside等时,仍承担着特定功能的分块,就可以使用 div 标签。

div标签和section标签一样,常常带有id属性。