当前位置: 首页 > article >正文

谷粒商城:性能压测JVM堆区

目录

Kit

Apache JMeter

VisualVM

堆内存

jvm内存模型

垃圾回收(Garbage Collection, GC)

新对象分配内存

GC步骤

MinorGC

性能优化

影响因素

优化

nginx动静分离

优化三级分类获取

Jvm参数配置堆区

测试


Kit

Apache JMeter

压力测试,模拟大量用户并发访问

VisualVM

监控、分析和调优 Java 应用程序的性能

VisualGC 插件:监控垃圾回收


堆内存

jvm内存模型

    堆区:

    所有对象实例和数组都存储在堆区,堆区是线程共享的,所有线程都可以访问堆中的对象。

    堆区结构:

    • 年轻代(Young Generation)

      • Eden 区:新创建的对象首先分配在这里。

      • Survivor 区:分为 From 和 To 区,存放经过一次 GC 后仍然存活的对象。

    • 老年代(Old Generation):存放长期存活的对象。

    • 元空间(Metaspace):存放类的元数据(如类定义、方法信息等)。

    垃圾回收(Garbage Collection, GC)

    新对象分配内存

    1. 新创建的对象首先分配在 Eden 区
    2. 当 Eden 区满时,触发 Minor GC
    3. MinorGC后,若Eden区放得下,则存入Eden区
    4. 若MinorGC后,Eden空间仍然不足,则为大对象,直接分配至Old区
    5. 若Old区空间不足,触发FullGC,对整个堆内存进行垃圾回收。

    GC步骤

    (1) 标记(Marking)

    • 遍历所有对象,标记哪些对象是存活的(被引用的),哪些是垃圾(未被引用)。

    • 从 GC Roots(如线程栈、静态变量等)开始,递归标记所有可达对象。

    (2) 清除(Sweeping)

    • 清除未被标记的垃圾对象,释放它们占用的内存空间。

    • 清除后,内存可能会产生碎片。

    (3) 压缩(Compacting)

    • 将存活的对象移动到内存的一端,整理出连续的内存空间,减少内存碎片。

    • 这一步不是所有 GC 算法都会执行(如 CMS 就不压缩)。

    MinorGC

    1. 标记 Eden 区和 From 区(当前活动的 Survivor 区)中的存活对象。
    2. 将存活的对象复制到 To 区(另一个 Survivor 区)。
    3. 清空 Eden 区和 From 区,From和To角色互换

    角色交换

    • 在下次 Minor GC 时,原来的 To 区 变为新的 From 区,而原来的 From 区 变为新的 To 区

    • 这种交换确保了每次 Minor GC 都有一个空的 Survivor 区用于存放存活对象。 

    对象晋升: 

    • 如果对象在 Survivor 区中经历了多次 Minor GC(默认是 15 次,可以通过 -XX:MaxTenuringThreshold 参数调整),它会被晋升到 老年代

     为什么需要两个 Survivor 区?

    • 减少内存碎片:通过复制算法,将存活对象从一个 Survivor 区复制到另一个 Survivor 区,可以整理内存,减少碎片。

    • 提高垃圾回收效率:每次 Minor GC 只需要处理 Eden 区和其中一个 Survivor 区,而不是整个年轻代。


    性能优化

    影响因素

    中间件

    • client - > nginx - > 网关 - > server        中间件越多,网络IO交互越多,性能损耗越大

    业务

    •  DB(MySQl优化)
    • 模板渲染(开启缓存)
    • 静态资源(动静分离)

    优化

    1.日志:仅报错

    logging:
      level:
        com.elysia.gulimall.product: error

    2. MySQL加索引

    3.thtmeleaf 开启ceche

    4.nginx动静分离

    5.优化业务逻辑

    6.通过 JVM 参数(如 -Xmx 和 -Xms)进行配置堆区

    nginx动静分离

    将js,css,img等静态资源存放在nginx中,静态资源由nginx返回。

    目录:

    nginx/html/static/index :css,js,img,json

    请求静态资源:

            gulimall.com/static/index/js/catalogLoader.js

    config:

    upstream gulimall {
        server 192.168.40.1:88;
    }
    
    server {
        listen       80;
        server_name  gulimall.com;
    
        location /static/ {
            root   /usr/share/nginx/html;
        }
    
        location / {
            proxy_set_header Host $host;
            proxy_pass http://gulimall;
        }
    
        #error_page  404              /404.html;
    
        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    
    }   
    

    优化三级分类获取

    原代码13行和19行多次查询数据库,获取子分类list

        @Override
        public List<CategoryEntity> getLevel1Category() {
            return this.list(new QueryWrapper<CategoryEntity>().eq("cat_level",1).eq("show_status",1));
        }
     
        @Override
        public Map<String, List<Level2CategoryVo>> getLevel2AndLevel3Category() {
            //一级分类
            List<CategoryEntity> level1 = this.getLevel1Category();
     
            Map<String, List<Level2CategoryVo>> categoryMap = level1.stream().collect(Collectors.toMap(k -> k.getCatId().toString(),v ->{
                //查询该一级分类下的二级分类
                List<CategoryEntity> level2List = this.list(new QueryWrapper<CategoryEntity>().eq("parent_cid", v.getCatId()));
     
                List<Level2CategoryVo> level2CategoryVos = null;
                if (level2List != null){
                     level2CategoryVos = level2List.stream().map(level2 -> {
                        //查询 二级分类 下的 三级分类
                        List<CategoryEntity> level3List = this.list(new QueryWrapper<CategoryEntity>().eq("parent_cid", level2.getCatId()));
     
                        List<Level2CategoryVo.Level3Category> collect = null;
                        if (level3List != null) {
                            collect = level3List.stream().map(level3 -> {
                                Level2CategoryVo.Level3Category level3Category = new Level2CategoryVo.Level3Category(level2.getCatId(), level3.getCatId(), level3.getName());
                                return level3Category;
                            }).collect(Collectors.toList());
                        }
                        Level2CategoryVo level2CategoryVo = new Level2CategoryVo(v.getCatId(), collect, level2.getCatId(), level2.getName());
                        return level2CategoryVo;
                    }).collect(Collectors.toList());
                }
                return level2CategoryVos;
            }));
            return categoryMap;
        }

    获取全部分类,通过filter获取某分类的子分类list

        @Override
        public Map<String, List<Level2CategoryVo>> getLevel2AndLevel3Category() {
            List<CategoryEntity> selectList = this.baseMapper.selectList(null);
    
            //一级分类
            List<CategoryEntity> level1 = this.getByParentCid(selectList,0L);
    
            Map<String, List<Level2CategoryVo>> categoryMap = level1.stream().collect(Collectors.toMap(k -> k.getCatId().toString(),v ->{
                //查询该一级分类下的二级分类
                List<CategoryEntity> level2List = getByParentCid(selectList,v.getCatId());
    
                List<Level2CategoryVo> level2CategoryVos = null;
                if (level2List != null){
                     level2CategoryVos = level2List.stream().map(level2 -> {
                        //查询 二级分类 下的 三级分类
                        List<CategoryEntity> level3List = getByParentCid(selectList,level2.getCatId());
    
                        List<Level2CategoryVo.Level3Category> collect = null;
                        if (level3List != null) {
                            collect = level3List.stream().map(level3 -> {
                                Level2CategoryVo.Level3Category level3Category = new Level2CategoryVo.Level3Category(level2.getCatId(), level3.getCatId(), level3.getName());
                                return level3Category;
                            }).collect(Collectors.toList());
                        }
                        Level2CategoryVo level2CategoryVo = new Level2CategoryVo(v.getCatId(), collect, level2.getCatId(), level2.getName());
                        return level2CategoryVo;
                    }).collect(Collectors.toList());
                }
                return level2CategoryVos;
            }));
            return categoryMap;
        }
    
        private List<CategoryEntity> getByParentCid(List<CategoryEntity> selectList,Long parentCid) {
            return selectList.stream().filter(categoryEntity -> {
                return categoryEntity.getParentCid().equals(parentCid); 
            } ).collect(Collectors.toList());
        }

    Jvm参数配置堆区

    将堆区固定为1024m,Eden区 512m

    测试

    全链路测试,请求首页

    50线程数,吞吐量:1100/s


    http://www.kler.cn/a/588976.html

    相关文章:

  • C# Winform 实现换肤,并自定义皮肤功能
  • C#—闭包详解
  • 群体智能优化算法-旗鱼优化算法 (Sailfish Optimizer, SFO,含Matlab源代码)
  • SQL Server表数据变更捕获的5种方法及实战对比
  • C++(初阶)(五)——类和对象(下)
  • 【极光 Orbit·STC8x】05. GPIO库函数驱动LED流动
  • 【区块链+乡村振兴】四川云龙肉牛产业数字化平台 | FISCO BCOS 应用案例
  • 操作系统八股文整理(一)
  • Celery在Django中的作用
  • day05_Java高级
  • 深度学习正则化技术之权重衰减法、暂退法(通俗易懂版)
  • 一款基于Python的从常规文档里提取图片的简单工具开发方案
  • 30、Vuex 为啥可以进行缓存处理
  • 【Leetcode 每日一题】3306. 元音辅音字符串计数 I
  • linux:环境变量,进程地址空间
  • 网络编程基础
  • 算法日记41:思维提升(最大gcd+好数组+简单的减法+球的颜色)
  • Cookie与Session详解
  • QuickAPI 和 DBAPI 谁更香?SQL生成API工具的硬核对比(一)
  • 从零实现区块链共识算法:用Python解锁去中心化世界的关键