目录
Kit
Apache JMeter
压力测试,模拟大量用户并发访问
VisualVM
监控、分析和调优 Java 应用程序的性能
VisualGC 插件:监控垃圾回收
堆内存
jvm内存模型
堆区:
所有对象实例和数组都存储在堆区,堆区是线程共享的,所有线程都可以访问堆中的对象。
堆区结构:
年轻代(Young Generation):
Eden 区:新创建的对象首先分配在这里。
Survivor 区:分为 From 和 To 区,存放经过一次 GC 后仍然存活的对象。
老年代(Old Generation):存放长期存活的对象。
元空间(Metaspace):存放类的元数据(如类定义、方法信息等)。
垃圾回收(Garbage Collection, GC)
新对象分配内存
- 新创建的对象首先分配在 Eden 区
- 当 Eden 区满时,触发 Minor GC
- MinorGC后,若Eden区放得下,则存入Eden区
- 若MinorGC后,Eden空间仍然不足,则为大对象,直接分配至Old区
- 若Old区空间不足,触发FullGC,对整个堆内存进行垃圾回收。
GC步骤
(1) 标记(Marking)
遍历所有对象,标记哪些对象是存活的(被引用的),哪些是垃圾(未被引用)。
从 GC Roots(如线程栈、静态变量等)开始,递归标记所有可达对象。
(2) 清除(Sweeping)
清除未被标记的垃圾对象,释放它们占用的内存空间。
清除后,内存可能会产生碎片。
(3) 压缩(Compacting)
将存活的对象移动到内存的一端,整理出连续的内存空间,减少内存碎片。
这一步不是所有 GC 算法都会执行(如 CMS 就不压缩)。
MinorGC
- 标记 Eden 区和 From 区(当前活动的 Survivor 区)中的存活对象。
- 将存活的对象复制到 To 区(另一个 Survivor 区)。
- 清空 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