案例15-创建大量对象导致cpu飙升

简介: 创建大量对象导致cpu飙升

一、背景介绍

       通过对于项目的部署使用发现,项目在使用不久会就发现非常的卡顿,内容加载不出来,或者加载非常慢的情况。但是每当我们构建完毕之后就不卡了,但是使用一点时间就又会出现卡顿的问题,带着这些现象对服务器进行了监控,看了服务对应的log日志。发现了一下两个问题。

1.从reids中读取数据拼接url地址,存在地址拼接不全的问题

6ba21e68bbf447b5828bd18b1c27252b.png2912a37dbab74055a584ae6dda124b6e.png

2.遇到并发的情况会产生大量的对象被创建,导致cpu飙升

5c19dcb5937140d5837b0076915f686a.png

二、分析问题

       两个问题是有前后关系的,当有并发请求到该业务的时候,由于keyList是一个全局变量,别的方法也会对他进行操作,这样就会有第一个方法还没有操作完第二个方法紧接着又进行了操作。最后导致我们拼接出来的key值在redis中是读取不到的。从而出现了拼接url地址确实的问题。对于产生大量的对象,也是由并发造成的,第一个对象刚创建出来,紧接着有创建了第二个对象。又因为对象复制给了一个公共变量,导致上一个对象失去了引用,等待垃圾回收器进行回收。从而cpu直线飙升。

d883eba990b54ab8a85a1f24bb1253da.png

三、如何解决

问题复现

1.操作类

public class Count {
    List<String> content=new ArrayList<>();
    public void getContent(){
        content = new ArrayList<>();
        content.add("1");
        content.add("2");
        content.add("3");
        content.add("4");
        System.out.println("wzill"+content);
    }
}

2.启动类,用100个线程模拟并发的情况

public class Client {
    public static void main(String[] args) {
        // 定义线程实现接口
        Runnable runnable = new Runnable(){
            Count  counter = new Count();
            @Override
            public void run() {
                try {
                    counter.getContent();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        };
        // 启动100个线程
        for( int i= 0;i< 100;i++) {
            new Thread(runnable).start();
        }
    }
}

3.运行结果,出现了数据混乱的情况。

a049edabf15d4f3b87f44923e31c1c49.png

解决问题

1.使用synchronized优化代码

某一时刻只有一个线程在执行,来确保线程数据的安全性。

public class Count {
    List<String> content=new ArrayList<>();
    public synchronized  void getContent(){
        content = new ArrayList<>();
        content.add("1");
        content.add("2");
        content.add("3");
        content.add("4");
        System.out.println("wzill"+content);
    }
}

fbafe8b9621040a99061f46b85dcf0a6.png

2.使用ThreadLocal

public class Count {
    private static ThreadLocal<List<String>> keyList = new ThreadLocal<List<String>>(){
        //对keyList进行初始化
        @Override public List<String> initialValue() {
            return new ArrayList<String>();
        }
    };
    public synchronized  void getContent(){
        keyList.get().add("1");
        keyList.get().add("2");
        keyList.get().add("3");
        keyList.get().add("4");
        System.out.println("wzill"+keyList.get());
    }
}

fa220da7e1c64268aa42a779321713c3.png

3.使用removeAll方法解决创建大量对象问题

//声明一个全局变量,ArrayList线程不安全
    List<String> keyList=new ArrayList<>();
    public synchronized void count2() throws InterruptedException {
        keyList.add("a");
        keyList.add("b");
        keyList.add("c");
        keyList.add("d");
        System.out.println("ARPRO"+keyList);
        //由于虽然使用synchronized锁住了count2()这个方法保证同一时刻只有一个线程执行
        //但是线程共享全局变量,所以当方法执行完成之后,需要将keyList的值进行还原
        keyList.removeAll(keyList);
       }

四、创建大量无用对象的危害

1.内存占用:每个对象都占用一定的内存空间,如果大量创建无用的对象,会导致内存占用过高,可能引发内存溢出或导致系统性能下降。

2.垃圾回收压力:创建大量无用的对象会增加垃圾回收的负担。垃圾回收器需要扫描和回收无用的对象,如果对象数量过多,会导致垃圾回收的频率增加,降低系统的响应速度。

3.性能下降:创建无用的对象会增加系统的负载,导致性能下降。特别是在循环或递归操作中,如果每次迭代都创建新的对象而不进行复用,会导致性能问题。

五、避免创建大量无用对象

1.对象池:使用对象池技术,提前创建一定数量的对象并保存在池中,需要时从池中获取对象并使用,使用完后放回池中供其他请求使用。这样可以避免频繁创建和销毁对象,提高性能。

2.对象复用:在循环或递归操作中,尽量复用已经创建的对象,避免每次迭代都创建新的对象。可以通过重置对象的状态或属性来复用对象。

3.缓存数据:对于一些频繁使用的数据,可以将其缓存起来,避免重复创建对象。可以使用缓存库或自定义缓存机制来实现数据的缓存。

4.使用对象池的数据结构:在某些情况下,可以使用对象池的数据结构来代替常规的数据结构。例如,使用对象池的字符串构建器代替字符串拼接操作,可以避免创建大量的临时字符串对象。

5.优化算法和代码逻辑:通过优化算法和代码逻辑,减少不必要的对象创建。例如,避免在循环中创建对象,尽量使用基本数据类型代替包装类型等。

六、总结提升

       使用全局变量时要慎重,考虑他的作用域以及影响的内容。添加必要的说明,因为作用域大了别人也可以使用这个变量,减少别人使用对之前的业务产生影响。

       大量创建无用的对象会导致内存占用、垃圾回收压力和性能下降等问题。为避免这些问题,可以使用对象池、对象复用、缓存数据、使用对象池的数据结构以及优化算法和代码逻辑等方法来减少对象的创建和销毁。


相关文章
|
6月前
|
设计模式 监控 安全
如何定位当生产环境CPU飙升的时候的问题
在当今的信息化时代,计算机系统在各行各业都发挥着重要的作用。然而,当生产环境中的CPU飙升时,系统性能会受到影响,甚至导致整个系统瘫痪。这不仅会对企业造成经济损失,还会对用户体验造成严重影响。因此,如何定位并解决生产环境中CPU飙升的问题,已成为众多企业和开发人员亟待解决的问题之一。本文旨在探讨如何定位生产环境中CPU飙升的问题,并提供相应的解决方案。通过了解CPU飙升的原因、定位方法以及解决方案,企业和开发人员可以更好地应对生产环境中出现的CPU飙升问题,提高系统性能和用户体验。
134 1
|
4月前
|
Java
手把手教你java CPU飙升300%如何优化
手把手教你java CPU飙升300%如何优化
61 0
|
4月前
|
监控 Java 中间件
FGC频繁导致CPU 飙升定位及JVM配置优化总结
FGC频繁导致CPU 飙升定位及JVM配置优化总结
168 0
|
6月前
|
监控 数据可视化 Java
Elasitcsearch CPU 使用率突然飙升,怎么办?
Elasitcsearch CPU 使用率突然飙升,怎么办?
136 1
|
5月前
|
SQL Java Linux
Linux系统cpu飙升到100%排查方案
Linux系统cpu飙升到100%排查方案
447 0
|
存储 缓存 监控
Java 经典面试解析:服务器卡顿、CPU飙升、接口负载剧增
解决这个问题的关键是要找到Java代码的位置。下面分享一下排查思路,以CentOS为例,总结为4步。 第1步,使用top命令找到占用CPU高的进程。 第2步,使用ps –mp命令找到进程下占用CPU高的线程ID。 第3步,使用printf命令将线程ID转换成十六进制数。 第4步,使用jstack命令输出线程运行状态的日志信息。
288 0
|
C++ 索引 Windows
调试实战——程序CPU占用率飙升,你知道如何快速定位吗?
程序CPU占用率飙升,你知道如何快速定位吗?
|
15天前
|
弹性计算 Kubernetes Perl
k8s 设置pod 的cpu 和内存
在 Kubernetes (k8s) 中,设置 Pod 的 CPU 和内存资源限制和请求是非常重要的,因为这有助于确保集群资源的合理分配和有效利用。你可以通过定义 Pod 的 `resources` 字段来设置这些限制。 以下是一个示例 YAML 文件,展示了如何为一个 Pod 设置 CPU 和内存资源请求(requests)和限制(limits): ```yaml apiVersion: v1 kind: Pod metadata: name: example-pod spec: containers: - name: example-container image:
100 1
|
24天前
|
存储 关系型数据库 MySQL
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
204 2
|
2月前
|
存储 关系型数据库 MySQL
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
149 5