当前位置:网站首页>A few lines of code can crash the system;
A few lines of code can crash the system;
2022-08-10 06:34:00 【If you never betray, dependent life and death I will】
Java程序是基于GC的,在启动初始,就申请了足量的内存池,再加上JIT等编译器的实时优化,速度并不比直接用C++语言写的慢.Java语言同时由于反射和可观测等特点,再加上JFR这种神器,在发生问题的时候比二进制文件更容易找到它的根源.
最近在看RCA(Root Cause Analysis)的东西,不小心发现了yCrash这么个东西.它的几段问题小代码写的非常典型,我们可以稍微看一下,来看看Java应用程序常见的几个崩溃场景.
1.堆空间溢出
OOM 一般是内存泄漏引起的,表现在 GC 日志里,一般情况下就是 GC 的时间变长了,而且每次回收的效果都非常一般.GC 后,堆内存的实际占用呈上升趋势.
下面的代码是死循环,持续向HashMap里塞数据,由于myMap属于GCRoots,始终得不到释放,所以它最终的结果就是OOM.
import java.util.HashMap;
public class OOMDemo {
static HashMap<Object, Object> myMap = new HashMap<>();
public static void start() throws Exception {
while (true) {
myMap.put("key" + counter, "Large stringgggggggggggggggggggggggggggg"
+ "ggggggggggggggggggggggggggggggggggggggggggggggggggggg"
+ "ggggggggggggggggggggggggggggggggggggggggggggggggggggg"
+ "ggggggggggggggggggggggggggggggggggggggggggggggggggggg"
+ "ggggggggggggggggggggggggggggggggggggggggggggggggggggg"
+ "ggggggggggggggggggggggggggggggggggggggggggggggggggggg"
+ "ggggggggggggggggggggggggggggggggggggggggggggggggggggg"
+ "ggggggggggggggggggggggggggggggggggggggggggggggggggggg"
+ "ggggggggggggggggggggggggggggggggggggggggggggggggggggg"
+ "ggggggggggggggggggggggggggggggggggggggggggggggggggggg"
+ "ggggggggggggggggggggggggggggggggggggggggggggggggggggg"
+ "ggggggggggggggggggggggggggggggggggggggggggggggggggggg"
+ counter);
++counter;
}
}
}
2.内存泄漏
内存泄漏和内存溢出是一个道理,不同的是它的语意.
内存溢出可能是由于请求量过高,或者真实的业务需求需要所造成的后果,而内存溢出属于未知的、超出期望的OOM情况.
我们可以使用上面同样的代码达到这个目的.
在现实情况中,内存泄漏通常都非常的隐蔽,需要借助Mat等工具才能找到根本原因.jmap、pmap等是常用的工具.
比如,如果你忘记了重写对象的hashCode和equals方法,就会产生内存泄漏.
//leak example : created by xjjdog 2022
import java.util.HashMap;
import java.util.Map;
public class HashMapLeakDemo {
public static class Key {
String title;
public Key(String title) {
this.title = title;
}
}
public static void main(String[] args) {
Map<Key, Integer> map = new HashMap<>();
map.put(new Key("1"), 1);
map.put(new Key("2"), 2);
map.put(new Key("3"), 2);
Integer integer = map.get(new Key("2"));
System.out.println(integer);
}
}
3.CPU飙升
直接一个死循环,就可以把CPU干死.
public class CPUSpikeDemo {
public static void start() {
new CPUSpikerThread().start();
new CPUSpikerThread().start();
new CPUSpikerThread().start();
new CPUSpikerThread().start();
new CPUSpikerThread().start();
new CPUSpikerThread().start();
System.out.println("6 threads launched!");
}
}
public class CPUSpikerThread extends Thread {
@Override
public void run() {
while (true) {
// Just looping infinitely
}
}
}
获取问题代码通常可以使用下面的方法.
(1)使用 top 命令,查找到使用 CPU 最多的某个进程,记录它的 pid.使用 Shift + P 快捷键可以按 CPU 的使用率进行排序.(2)再次使用 top 命令,加 -H 参数,查看某个进程中使用 CPU 最多的某个线程,记录线程的 ID.(3)使用 printf 函数,将十进制的 tid 转化成十六进制.(4)使用 jstack 命令,查看 Java 进程的线程栈.(5)使用 less 命令查看生成的文件,并查找刚才转化的十六进制 tid,找到发生问题的线程上下文.
4.线程泄漏
线程资源是昂贵的.如果你不停的创建线程,系统资源很快就会被耗尽.下面的代码一直不停的创建线程,如果同时请求压力比较大的话,多数能搞死宿主机.
public class ThreadLeakDemo {
public static void start() {
while (true) {
new ForeverThread().start();
}
}
}
public class ForeverThread extends Thread {
@Override
public void run() {
// Put the thread to sleep forever, so they don't die.
while (true) {
try {
// Sleeping for 10 minutes repeatedly
Thread.sleep(10 * 60 * 1000);
} catch (Exception e) {
}
}
}
}
这是暴力啊,这和每个请求创建一个线程,或者创建一个线程池的后果是一样的.xjjdog这里还有两篇关联的线程泄漏文章.
5.死锁
死锁代码一般不会发生,但一旦发生还是非常严重的,相关的业务可能就跑不动了.
public class DeadLockDemo {
public static void start() {
new ThreadA().start();
new ThreadB().start();
}
}
public class ThreadA extends Thread {
@Override
public void run() {
CoolObject.method1();
}
}
public class ThreadB extends Thread {
@Override
public void run() {
HotObject.method2();
}
}
public class CoolObject {
public static synchronized void method1() {
try {
// Sleep for 10 seconds
Thread.sleep(10 * 1000);
} catch (Exception e) {
}
HotObject.method2();
}
}
public class HotObject {
public static synchronized void method2() {
try {
// Sleep for 10 seconds
Thread.sleep(10 * 1000);
} catch (Exception e) {
}
CoolObject.method1();
}
}
死锁属于比较严重的一种情况,jstack 会以明显的信息进行提示.当然,关于线程的 dump,也有一些线上分析工具可以使用.比如fastthread,但也需要你先了解这些情况发生的意义.
6.栈溢出
栈溢出不会造成 JVM 进程死亡,危害“相对较小”.下面是一个简单的模拟栈溢出的代码,只需要递归调用就可以了.
public class StackOverflowDemo {
public void start() {
start();
}
}
7.Blocked线程
BLOCKED是一个比较严重的线程状态,当后端的服务处理时间非常长,请求的线程就会进入等待状态.这时候通过jstack来获取堆栈,就会发现线程处于阻塞状态.它阻塞在对锁的获取上(wating to lock)
public class BlockedAppDemo {
public static void start() {
for (int counter = 0; counter < 10; ++counter) {
// Launch 10 threads.
new AppThread().start();
}
}
}
public class AppThread extends Thread {
@Override
public void run() {
AppObject.getSomething();
}
}
public class AppObject {
public static synchronized void getSomething() {
while (true) {
try {
Thread.sleep(10 * 60 * 1000);
} catch (Exception e) {
}
}
}
}
一旦频繁发生这种情况,就证明你的程序相应太慢了.如果CPU资源还有剩余,可以尝试着增加请求的线程数,比如tomcat的最大线程数.
End
以上就是对于Java常见故障的几段小代码分析,大部分的故障都逃不出这些场景.故障的排查通常都非常耗费精力,而且你得有线上权限.怎样做一些好用的工具,把这些复杂性屏蔽在后面,才是我们所想要的.
文章来源1,2,3
边栏推荐
猜你喜欢
随机推荐
【论文解读】滴滴智能派单-KDD2018 Large-Scale Order Dispatch in On-Demand Ride-Hailing
MySQL事务隔离级别
Hypervisor, KVM, QEMU总结
程序员的十楼层。看看自己在第几层。PS:我的目标是:30岁第四层
OSPF的dr和bdr
JS中初始化对象为null和空对象的区别
OpenGL学习笔记(LearnOpenGL)-第六部分 变换
Qt绘制椭圆曲线的角度问题(离心角和旋转角)
【Day10】进程管理命令
3.事务篇【mysql高级】
CAP介绍
I would like to ask you guys, when FLink SQL reads the source, specify the time field of the watermark. If the specified field is in the grid
Can‘t find bundle for base name jdbc, locale zh_CN解决方法
如何在AdsPower中设置YiLu代理?
一种奇怪的函数声明写法
几行代码就可以把系统高崩溃;
网页安全证书错误但无法安装证书的解决办法
2022河南萌新联赛第(五)场:信息工程大学 B - 交通改造
UnityShader入门精要-阴影
Qt滚动条(QScrollBar)圆角样式问题跟踪