【Java】解决程序堆内存正常,但实际内存过高的问题

简介: 最近遇到一个很头疼的问题,用Java写的工具运行几天后,内存涨到8G以上,我用jvisualvm监控发现堆内存持续稳定未出现泄漏,然后就各种搜问题,最后经过1周多时间排查处理,最后终于修复了,因此记录一下。

【Java】解决程序堆内存正常,但实际内存过高的问题


一、前言


   最近遇到一个很头疼的问题,用Java写的工具运行几天后,内存涨到8G以上,我用jvisualvm监控发现堆内存持续稳定未出现泄漏,然后就各种搜问题,最后经过1周多时间排查处理,最后终于修复了,因此记录一下。


二、问题与解决方案


   1. jni 内存泄漏问题


       由于我的Java需要调用C++语言写的模块,因此采用了jni调用,将java中的byte[]传给C++进行处理,最后返回已处理的byte[]数据,这里遇到了第一个内存泄漏问题。

// jni c++
jbyte* bytedata = env->GetByteArrayElements(data, 0);
// 对bytedata进行处理后返回
//c_decode_data(bytedata) 
jbyteArray result = env->NewByteArray(len);
env->SetByteArrayRegion(result, 0, len, bytedata);
env->ReleaseByteArrayElements(data, bytedata, 0);
return result


      上面的代码运行会产生内存泄漏,具体原因如下:


           1).  使用env->GetByteArrayElements后必须调用env->ReleaseByteArrayElements,其他GetXXX(...)也是类似。


           2).  使用env->NewByteArray生成的对象需要调用env->

DeleteLocalRef删除引用,其他NewXXX(...)也是类似。


  2. Java堆内存正常,但是实际使用内存过高


       使用pmap -x 打印内存信息时,发现有很多64MB的[anon]内存块,如下图(图片来自网络截取)。


微信截图_20220424101213.png微信截图_20220424101228.png


       引起上面的原因很多地方都说是glibc自2.11以后的版本导致的,glibc使用arena的memory pool解决多线程内存分配竞争的问题。解决方法是在启动Java程序前设置MALLOC_ARENA_MAX=1或者其他比较小的值来解决,推荐值为4。也可以通过使用tcmalloc库来规避这个问题。


   3. glibc的另一个问题


       C语言中的malloc和free在不同平台有不同的实现,在linux上由glibc实现,glibc内部维护了一个内存池,在调用free的时候内存只会归还给glibc,再由glibc决定是否归还给系统。如果想立即归还给系统可以主动调用malloc_trim(0)


三、总结


   以上就是本次解决Java内存泄漏问题学到的几个知识点,我也只是大致了解了一下,对其原理并未深入理解。大家如果遇到同样的问题时,可以尝试我介绍的方法,如果能够深入理解原理就更好了。

欢迎微信搜索"游戏测试开发"关注一起沟通交流。



相关文章
|
7月前
|
Java C语言
课时8:Java程序基本概念(标识符与关键字)
课时8介绍Java程序中的标识符与关键字。标识符由字母、数字、下划线和美元符号组成,不能以数字开头且不能使用Java保留字。建议使用有意义的命名,如student_name、age。关键字是特殊标记,如蓝色字体所示。未使用的关键字有goto、const;特殊单词null、true、false不算关键字。JDK1.4后新增assert,JDK1.5后新增enum。
114 4
|
7月前
|
Java 编译器
课时7:Java程序基本概念(注释)
课时7介绍了Java程序中的注释。编程语言有其语法和语义,注释有助于理解代码需求,防止断档。Java支持三类注释:单行(//)、多行(/* */)和文档注释(/** */)。注释不会被编译器编译。范例中展示了如何在代码中使用注释,并强调了注释对项目文档管理的重要性。
114 3
|
7月前
|
存储 Java 数据库连接
【YashanDB知识库】Java程序调用存储过程,在提取clob时报YAS-00004
【YashanDB知识库】Java程序调用存储过程,在提取clob时报YAS-00004
|
7月前
|
搜索推荐 Java Android开发
课时146:使用JDT开发Java程序
在 Eclipse 之中提供有 JDT环境可以实现java 程序的开发,下面就通过一些功能进行演示。 项目开发流程
258 0
|
9月前
|
传感器 安全 算法
消防救援支队消防员单兵装备智能养护舱电机驱动java版程序(二)
本文探讨消防救援中智能养护舱电机驱动的Java程序设计,作为系列文章第二部分。通过自动化和智能化手段,智能养护舱提升了装备维护效率与准确性。文章详细介绍了电机驱动模块的设计与实现,包括硬件选型、PID控制策略、安全保护机制及Java程序架构,确保电机精确控制、稳定性和安全性。未来将优化功能并引入智能算法和物联网技术,进一步提升装备维护智能化水平。
|
9月前
|
IDE Java 开发工具
消防救援支队消防员单兵装备智能养护舱点击驱动java版程序(一)
智能消防作战服架通过电机驱动系统提升消防员作业效率和安全性。本文介绍基于Java的电机驱动程序开发,涵盖硬件准备、软件环境搭建及驱动程序实现。重点包括串口通信配置、电机控制类设计与控制逻辑实现,确保电机高效稳定运行。通过正确配置通信协议和串口参数,并添加异常处理机制,保障系统的安全性和可靠性。
|
9月前
|
监控 Java 中间件
8G的容器Java堆才4G怎么就OOM了?
本文记录最近一例Java应用OOM问题的排查过程,希望可以给遇到类似问题的同学提供参考。
|
7月前
|
Java 开发工具
课时5:第一个Java程序
课时5介绍了编写第一个Java程序的步骤,包括创建Hello.java文件、编写“Hello World”代码、编译和运行程序。主要内容有:1) 新建并编辑Hello.java;2) 编译Java源文件生成.class文件;3) 通过命令行解释执行Java程序;4) 解释主方法的作用及信息输出操作。本课强调了类定义、文件命名规则和基本程序结构的重要性,并建议初学者使用记事本编写代码以熟悉基础语法。
115 0
|
10月前
|
算法 Java
堆内存分配策略解密
本文深入探讨了Java虚拟机中堆内存的分配策略,包括新生代(Eden区和Survivor区)与老年代的分配机制。新生代对象优先分配在Eden区,当空间不足时执行Minor GC并将存活对象移至Survivor区;老年代则用于存放长期存活或大对象,避免频繁内存拷贝。通过动态对象年龄判定优化晋升策略,并介绍Full GC触发条件。理解这些策略有助于提高程序性能和稳定性。
|
10月前
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
236 5

热门文章

最新文章