一,前提准备
最基本前提:你需要有liunx环境,如果没有请参考其它文献在自己得到local建立一个虚拟机去进行测试。
有了虚拟机之后,你还需要安装jdk和配置环境变量
1. 安装JDK(以OpenJDK 17为例)
下载JDK
# 进入用户主目录
cd ~# 下载OpenJDK 17(以.tar.gz包为例)
wget https://download.java.net/java/GA/jdk17.0.2/dfd4a8d0985749f896bed50d7138ee7f/8/GPL/openjdk-17.0.2_linux-x64_bin.tar.gz# 或Oracle JDK(需官网同意许可协议)
# wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz
这里就已经下好了
解压并安装
# 创建安装目录(需sudo权限)
sudo mkdir -p /usr/local/java# 解压JDK到目标目录
sudo tar -xzvf openjdk-17.0.2_linux-x64_bin.tar.gz -C /usr/local/java# 查看解压后的目录名(例如jdk-17.0.2)
ls /usr/local/java
命令解释
mkdir -p /path/to/directory
- 递归创建目录:当你指定的路径中某些目录不存在时,
mkdir -p
会自动创建这些缺失的父目录。 - 不会报错:如果目录已经存在,
mkdir -p
不会报错,命令会正常执行。
tar -xzvf openjdk-17.0.2_linux-x64_bin.tar.gz -C /usr/local/java
tar -xzvf
是用来解压 .tar.gz
或 .tgz
文件的命令。
x
:表示 解压(extract)。z
:表示 通过 gzip 压缩格式进行解压(unzip the gzip file)。v
:表示 显示详细信息,在解压时列出文件名。这个选项是可选的,用来查看解压过程中有哪些文件被处理。f
:表示 指定文件,后面需要跟要解压的.tar.gz
文件名。
2, 配置环境变量
编辑环境变量文件
# 打开用户环境变量配置文件(以bash为例)
nano ~/.bashrc # 或 ~/.bash_profile、~/.zshrc(根据Shell类型)
命令解释
nano
:是一个命令行下的文本编辑器。它非常简单,适合快速编辑文件。~/.bashrc
:是当前用户主目录下的 Bash 配置文件。每当你打开一个新的终端时,Bash shell 会加载该文件。在这个文件中,你可以设置环境变量、命令别名、shell 提示符样式等。
添加以下内容到文件末尾
需要注意,不能有空格
# 手动安装配置
export JAVA_HOME=/usr/local/java/jdk-17.0.2 # 替换为实际解压路径
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar# 包管理器安装配置(若使用方式2)
# export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
# export PATH=$JAVA_HOME/bin:$PATH
使配置生效
source ~/.bashrc # 根据实际配置文件选择
验证安装
# 检查Java版本
java -version# 输出应类似:
openjdk version "17.0.2" 2022-01-18
OpenJDK Runtime Environment (build 17.0.2+8-86)
OpenJDK 64-Bit Server VM (build 17.0.2+8-86, mixed mode)# 检查JAVA_HOME
echo $JAVA_HOME
# 输出:/usr/local/java/jdk-17.0.2
自动化脚本安装示例:
#!/bin/bash
# 一键安装并配置JDK 17(手动方式)
JDK_URL="https://download.java.net/java/GA/jdk17.0.2/dfd4a8d0985749f896bed50d7138ee7f/8/GPL/openjdk-17.0.2_linux-x64_bin.tar.gz"
INSTALL_DIR="/usr/local/java"# 下载并解压
sudo mkdir -p $INSTALL_DIR
wget $JDK_URL -O /tmp/jdk17.tar.gz
sudo tar -xzvf /tmp/jdk17.tar.gz -C $INSTALL_DIR# 配置环境变量
echo "export JAVA_HOME=$INSTALL_DIR/$(ls $INSTALL_DIR)" >> ~/.bashrc
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> ~/.bashrc
source ~/.bashrc# 验证
java -version
二,Java代码准备
编写一个简单的让程序不断创建新对象,然后GC在不停地回收,但是又回收不掉地样例。并打包为jar包给到我们的liunx服务器。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;public class Main {// 内存泄漏:让对象无法被GC回收private static final List<byte[]> LEAK_LIST = new ArrayList<>();// CPU密集型任务开关private static final AtomicBoolean RUNNING = new AtomicBoolean(true);public static void main(String[] args) {// 内存泄漏线程:持续分配内存,触发频繁GCnew Thread(() -> {while (true) {// 每次分配1MB内存(不会被回收)LEAK_LIST.add(new byte[1024 * 1024]);try {Thread.sleep(100); // 控制内存分配速度} catch (InterruptedException e) {e.printStackTrace();}}}, "MemoryLeakThread").start();// CPU密集型线程:死循环计算,占用CPUnew Thread(() -> {while (RUNNING.get()) {// 无意义但耗CPU的计算double result = 0;for (int i = 0; i < 100000; i++) {result += Math.sin(i) * Math.cos(i);}}}, "CpuIntensiveThread").start();}
}
打包为可执行jar包
将jar包给到linux
这里我用的vmware的文件共享区进行的共享的。
在虚拟机的设置里面
在选项这里进行配置一下
vmware的文件共享区在Linux中的/mnt/hgfs/ 文件下
如果/mnt/hgfs
目录为空
-
可能原因:
-
VMware Tools未正确安装。
-
共享文件夹未启用或配置错误。
-
自动挂载服务未运行。
-
解决方案:
步骤1:检查VMware Tools状态
# 查看服务是否运行(Ubuntu/Debian)
systemctl status vmware-tools.service# 重启服务(如果未运行)
sudo systemctl restart vmware-tools.service如果未安装 VMware Tools则
# 检查 open-vm-tools 状态
systemctl status open-vm-tools
# 若服务存在且未运行,
启动服务
sudo systemctl start open-vm-tools
sudo systemctl enable open-vm-tools
步骤2:手动挂载共享文件夹(临时生效)
# 创建挂载点(若目录不存在)
sudo mkdir -p /mnt/hgfs# 手动挂载
sudo vmhgfs-fuse .host:/ /mnt/hgfs -o allow_other#如果因为权限不足,可以加权限
sudo usermod -aG root rojer(rojer是用户名)
步骤3:配置开机自动挂载(永久生效)
# 编辑/etc/fstab文件
sudo nano /etc/fstab# 添加以下行(保存退出)
.host:/ /mnt/hgfs fuse.vmhgfs-fuse allow_other,defaults 0 0# 重新挂载
sudo mount -a
准备调试工具
使用阿里的arthas
arthas/README_CN.md at master · alibaba/arthas · GitHub
下载 Arthas 的 arthas-boot.jar
文件
curl -O https://arthas.aliyun.com/arthas-boot.jar
三,开始测试
使用以下JVM参数启动程序,加速问题暴露(JDK1.8版本):
java -Xms100m -Xmx100m # 限制堆内存为100MB,加速GC触发
-XX:+UseG1GC # 使用G1垃圾回收器(观察GC日志)
-XX:+PrintGCDetails # 打印GC详细信息
-XX:+PrintGCDateStamps # 显示GC时间戳-jar
GcDemo.jarJava 9 及以上改用
-Xlog:gc
java -Xms100m -Xmx100m -XX:+UseG1GC -Xlog:gc -jar GCdemo.jar
启动之后问题出现
现象验证方法
观察GC频繁:
1,使用ps -ef | grep java
找到java进程的pid
2,通过top命令观察java进程的实时状态
2,使用 jstat -gc <pid> 1000(每秒刷新):
观察GC回收状况,从上面命令可以看出内存资源并不紧张,所以这里只做展示
YGC(Young GC次数)和 FGC(Full GC次数)会快速上升。
从上面可以看出fullGC的次数才3次,且回收时间不大,说明内存情况很健康。又从top中看到cpu资源已经打满,说明有程序在疯狂占用cpu的计算资源。
这里解释一下上图的代表意思
|
详细观察cpu的占用情况
top -p <pid>
然后 按 H
键(大写的 H)切换到线程视图:
- 按下
H
键后,top
会从显示进程列表切换到显示线程列表,每个线程会显示为一个单独的条目。
使用Arthas诊断:
# 1. 启动Arthas
java -jar arthas-boot.jar
这里选择所要监控的程序。
需要注意,如果出现连接异常,且
~/logs/arthas/arthas.log
路径下log中有
Arthas server agent start...
java.lang.OutOfMemoryError: Java heap space
说明你当前的机器在不断消耗内存,导致arthas起不起来!
因为在jvm初始化的时候,这个阶段非常吃内存。
查看线程CPU占用
# 当前系统的实时数据面板,按 ctrl+c 退出。
dashboard
这里可以看出cpu已经打满,但是堆内存情况良好。
追踪高cpu线程
thread
参数名称 参数说明 id 线程 id [n:] 指定最忙的前 N 个线程并打印堆栈 [b] 找出当前阻塞其他线程的线程 [i <value>
]指定 cpu 使用率统计的采样间隔,单位为毫秒,默认值为 200 [--all] 显示所有匹配的线程
这里就已经能定位到问题了。我这个样例比较简单,就一个main类。大家可以构建复杂一点的项目,进行一点点调试看问题。
使用java原生的也能定位到问题。可能比较繁琐一点。
使用 jstack
获取线程堆栈信息
使用 jvisualvm
进行图形化分析
等等。
其它arthas常用方法
- trace:追踪方法调用的堆栈,帮助你查看方法内部的执行情况。
- monitor:监控方法的执行,特别是性能问题,像慢方法。
- stack:查看线程堆栈,特别是线程阻塞和死锁问题。
- watch:监控方法调用,查看参数和返回值,帮助定位参数错误。
- heapdump:生成堆转储文件,帮助分析内存问题。
- jstack:查看 Java 进程的堆栈信息,诊断崩溃或线程死锁。
参考官方文档使用说明!
auth | arthas