二、调优案例分析

二、调优案例分析,第1张

二、调优案例分析 1、高性能的硬件上的程序部署策略 1.1、目前主要有两种方式: 1.1.1、通过64位JDK来使用大内存

        利:可以减少FUll GC对频率

        弊:内存回收导致的长时间停顿、相同的程序在64位的JDK中消耗的内存普遍高于32位的JDK(由于指针膨胀和数据类型对齐填充等因素导致)、需要保证程序足够稳定,因为这种应用如果产生了堆溢出几乎无法产生堆转存储快照(因为要产生十几GB乃至更大的Dump文件),就算产生了快照几乎无法进行分析;

1.1.2、使用若干个32位虚拟机建立逻辑集群来利用硬件资源

具体的做法就是:一台物理机器上启动多个应用服务进程,每个服务进程分配不同的端口,然后在搭配一个负载均衡器,用反向代理的方式来分配访问请求;

        利:可以充分的利用物理资源

        弊:1)尽量避免节点进行全局资源的竞争,例如磁盘的竞争,如果各个节点同时访问同一个磁盘的话,很容易导致IO异常

                2)很难最高效的利用一些资源,例如连接池,每个节点都有自己的连接池,会存在一些节点连接池满了,而一些节点的连接池还有空闲

                3)各个节点受到32位的内存限制,在32位的window *** 作系统中,每个进程只能使用2GB的内存(一般不到2GB,还有堆外内存的开销),在某些Linux或者Unix系统(Solaris)中可以提升到4GB,(2~32)内存限制;

1.2、问题记录:

为什么32位机器上有最大4GB内存限制?网上的详细解说:

32位系统最大只能支持4GB内存之由来 - Matrix海子 - 博客园

2、集群间同步导致的内存溢出

在某些集群同步数据的时候,存在信息传输失败需要重新发送请求的可能,一些同步机制中,在确保所有注册的节点都同步完成,并发送回了响应信息前,这些同步数据就会保存在内存中;由于集群间数据同步过于频繁,当网络情况不能满足传输的要求时,重发的数据在内存中不断的堆积,就会发生内存溢出;

3、堆外内存导致的内存溢出 3.1、内存分析:

在JDK8中引入了NIO的同时引入了Direct Memory的概念,Direct Memory并不会占用堆内存,但是受到总的物理内存的限制,例如:在32位的window *** 作系统后中,只能分配了2GB内存,堆内存占用了1.6GB,那么Direct Memory就只能占用0.4GB的内存;在写IO程序(Netty),经常使用ByteBuffer来读取写入数据(基于通道channel和缓存Buffer)的IO方式,可以使用Native本地函数库直接使用堆外内存,然后使用Java堆中的DirectByteBuffer对象作为 *** 作这块内存的引用;避免了Java堆和native堆的来回切换,从而提升性能;

如果我们不断的使用对外内存,堆内存很少使用,并且配置了-XX:+DisableExplicitGC,JVM就不会进行GC,DirectByteBuffer就不会被回收,当需要分配堆外内存的时候,就没有堆外内存空间可供分配,就会抛出OutOfMemoryError;

配置直接堆外内存大小参数:-XX:MaxDirectMemorySize;

3.2、除了DirectMemory会占用内存外,还有:

        线程堆栈:-Xss,当纵向无法分配,就是无法分配新的栈帧就会抛出StackOverflowError;无法横向分配,就是无法建立新的线程,就会抛出OutOfMemoryError:unable to create new native thread;

        Socket缓存区:每一个Socket连接都会有Receive(37KB)和Send(25KB)两个缓冲区,如果连接多的话就会占用较大的内存;IOException: Too mang open files 异常

        JNI代码:如果代码中使用了JNI调用本地库,这部分占用的内存也不在堆内存中

        虚拟机和GC:虚拟机和GC代码执行的时候也要消耗内存;

3.3、问题记录:

        堆外内存导致的溢出错误,使用-XX:+DisableExplicitGC开关,会导致System.gc()变成空调用,没有任何对作用?网上的详细解说:

-XX:+DisableExplicitGC弊端_pocher的博客-CSDN博客_disableexplicitgc

4、外部命令导致内存溢出

在代码中使用Runtime.getRuntime().exec()方法调用一个外部shell脚本命令;Java虚拟机在执行这个命令,需要先克隆一个和当前虚拟机拥有一样环境变量的进程,再使用这个进程去执行这个命令,最后退出;这个过程是非常消耗资源的,虽然命令本身执行的速度很快,但是频繁的调用的话,系统的消耗是很大的,CPU和内存的消耗负担都很大;

5、服务器JVM进程崩溃

远程调用进行数据传输,由于传输数据量大,传输频繁,网络拥堵,两边的服务器速度不对等,时间越长,导致在等待的线程和Socket连接越来越多,超过系统能承受的能力,就会使得虚拟机进程崩溃;可以使用消息队列进行异步调用;

6、不恰当的数据结构导致内存的占用过大 6.1、基础数据占用内存情况:

byteboolean : 1字节

shortchar: 2字节

intfloat: 4字节

longdouble: 8字节

6.2、java对象:对象头,实例数据、对齐填充 6.2.1 对象头占用空间

1. 在32位系统下,存放Class指针的空间大小是4字节,MarkWord是4字节,对象头为8字节。

2. 在64位系统下,存放Class指针的空间大小是8字节,MarkWord是8字节,对象头为16字节。

3. 在64位开启指针压缩的情况下 -XX:+UseCompressedOops,存放Class指针的空间大小是4字节,MarkWord是8字节,对象头为12字节。

4. 如果对象是数组,那么额外增加4个字节(对象头中必须有一块数据用于记录数组长度,也就是一个int类型的对象,占4字节。)

6.2.2 实例数据

实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是在子类中定义的,都需要记录起来。

6.2.3 对齐填充

最后一块对齐填充空间并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。这是由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是8字节的整数倍。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5700267.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存