JVM 堆外内存详解

Java 进程内存占用除了JVM 运行时数据区,还有直接内存(Direct Memory)区域及 JVM 程序自身也会占用内存

  • 直接内存(Direct Memory)区域:直接内存通过使用Native堆外内存来存储数据,这意味着数据不会被JVM的垃圾回收机制自动回收。与JVM堆内存相比,直接内存的分配和释放成本较高,因为它涉及与操作系统交互和内存管理的开销,也可能导致OOM异常出现
  • JVM 程序自身:JVM本身是个本地程序,还需要其他的内存去完成各种基本任务,比如,JIT Compiler 在运行时对热点方法进行编译,就会将编译后的方法储存在 Code Cache 里面;GC 等功能需要运行在本地线程之中,类似部分都需要占用内存空间

Java 进程内存占用

JVM内存区域划分详见 Java 内存区域与内存溢出异常

堆外内存

JVM 的堆外内存是指分配在JVM堆之外的内存空间,它不受JVM的垃圾回收机制管理。 以下是几种常见的JVM堆外内存:

  1. 直接字节缓冲区(Direct ByteBuffers):Direct ByteBuffer是JVM堆外内存的一种形式,它通过使用Native堆外内存来存储数据。
  2. NIO(New I/O)内存映射文件(Memory-mapped Files):NIO提供了一种将文件映射到内存的方式,这种内存映射文件将文件的内容直接映射到堆外内存中,可以通过内存访问的方式来读写文件。
  3. JNI(Java Native Interface):JNI允许Java程序与本地代码进行交互,可以在本地代码中分配和管理堆外内存。

堆外内存可以使用Native Memory Tracking 或 Arthas memory 进行监控及诊断

直接字节缓冲区

在实际使用中,Java 会尽量对 Direct Buffer 仅做本地 IO 操作,对于很多大数据量的 IO 密集操作,可能会带来非常大的性能优势,因为:

  • Direct Buffer 可以通过ByteBuffer.allocateDirect()方法来创建,它的数据存储在堆外内存中,生命周期内内存地址都不会再发生更改,进而内核可以安全地对其进行访问,很多 IO 操作会很高效
  • 减少了堆内对象存储的可能额外维护工作,所以访问效率可能有所提高

Direct Buffer 创建和销毁过程中,都会比一般的堆内 Buffer 增加部分开销,所以通常都建议用于长期使用、数据较大的场景。

可以使用JVM参数设定直接内存限制

-XX:MaxDirectMemorySize=512M

大多数垃圾收集过程中,都不会主动收集 Direct Buffer,它的垃圾收集过程,就是基于 Cleaner(一个内部实现)和幻象引用(PhantomReference)机制,其本身不是 public 类型,内部实现了一个 Deallocator 负责销毁的逻辑。对它的销毁往往要拖到full GC的时候,所以使用不当很容易导致OutOfMemoryError

Direct Buffer 回收方式:

  • 在应用程序中,显式地调用System.gc()来强制触发。
  • 另外一种思路是,在大量使用 Direct Buffer 的部分框架中,框架会自己在程序中调用释放方法(Netty 就是这么做的,有兴趣可以参考其实现PlatformDependent0)
  • 重复使用 Direct Buffer

NIO

Java NIO(New I/O)是Java提供的一套用于高效处理I/O操作的API,引入自JDK 1.4版本。相对于传统的Java I/O(IO流)API,Java NIO提供了更灵活、更高效的非阻塞I/O操作方式,适用于构建高性能的网络应用程序。

Java NIO的核心概念包括以下几个部分:

  • 通道(Channel):通道是数据源和数据目标之间的连接,可以通过通道读取和写入数据。通道可以是双向的,可以从通道中读取数据,也可以向通道中写入数据
  • 缓冲区(Buffer):缓冲区是一个固定大小的数据容器,用于存储读取和写入的数据。通过缓冲区可以更高效地读写数据,避免频繁的数据拷贝操作。缓冲区可以读取和写入不同类型的数据,如字节、字符、整数等
  • 选择器(Selector):选择器是用于多路复用非阻塞I/O操作的组件。可以通过选择器同时管理多个通道,使得单线程可以处理多个通道的I/O操作,提高系统的性能和吞吐量

NIO提供了一种将文件映射到内存的方式,这种内存映射文件将文件的内容直接映射到堆外内存中。这种方式在处理大型文件时可以提供更高的性能和效率

JNI

使用JNI(Java Native Interface)可以在Java程序中通过调用本地代码来使用JVM堆外内存。JNI提供了一种机制,使得Java程序可以与本地代码进行交互,调用本地代码中的函数和访问本地内存

通过JNI,Java程序可以直接访问和操作本地内存,例如在C或C++中使用malloc()free()函数进行内存分配和释放

JNI操作JVM堆外内存具体步骤
  1. 定义本地方法:在Java类中声明本地方法,使用native关键字标记。
public class NativeMemoryExample {public native void allocateMemory(int size);public native void freeMemory();
}
  1. 生成本地方法的头文件:使用Java的javac命令编译Java源文件,然后使用javah命令生成本地方法的头文件。
javac NativeMemoryExample.java
javah NativeMemoryExample

这将生成名为NativeMemoryExample.h的头文件

  1. 实现本地方法:在本地代码中实现Java类中声明的本地方法。在本地方法中可以使用C/C++等编程语言来操作堆外内存
#include "NativeMemoryExample.h"
#include <stdlib.h>JNIEXPORT void JNICALL Java_NativeMemoryExample_allocateMemory(JNIEnv *env, jobject obj, jint size) {void *buffer = malloc(size);// 使用buffer进行堆外内存操作
}JNIEXPORT void JNICALL Java_NativeMemoryExample_freeMemory(JNIEnv *env, jobject obj) {// 释放之前分配的堆外内存free(buffer);
}
  1. 编译本地代码:使用C/C++编译器将本地代码编译为共享库(或动态链接库)
gcc -shared -fpic -o libNativeMemoryExample.so NativeMemoryExample.c
  1. 加载本地库:在Java程序中使用System.loadLibrary()方法加载本地库
public class Main {static {System.loadLibrary("NativeMemoryExample");}public static void main(String[] args) {NativeMemoryExample example = new NativeMemoryExample();example.allocateMemory(1024);  // 调用本地方法分配堆外内存// ...example.freeMemory();  // 调用本地方法释放堆外内存}
}

通过以上步骤,Java程序可以使用JNI调用本地方法,在本地代码中进行对JVM堆外内存的分配和释放操作。需要注意的是,在使用JNI时应谨慎管理内存,避免内存泄漏和溢出,确保正确地释放分配的堆外内存


参考资料:

  1. Java Native Interface
  2. Direct Buffer
  3. Native Memory Tracking

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/160595.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大数据平台实践之CDH6.2.1+spark3.3.0+kyuubi-1.6.0

前言&#xff1a;关于kyuubi的原理和功能这里不做详细的介绍&#xff0c;感兴趣的同学可以直通官网&#xff1a;https://kyuubi.readthedocs.io/en/v1.7.1-rc0/index.html 下载软件版本 wget http://distfiles.macports.org/scala2.12/scala-2.12.16.tgz wget https://archi…

pikachu_php反序列化

pikachu_php反序列化 源代码 class S{var $test "pikachu";function __construct(){echo $this->test;} }//O:1:"S":1:{s:4:"test";s:29:"<script>alert(xss)</script>";} $html; if(isset($_POST[o])){$s $_POST[…

基于python人脸性别年龄检测系统-深度学习项目

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介简介技术组成1. OpenCV2. Dlib3. TensorFlow 和 Keras 功能流程 二、功能三、系统四. 总结 一项目简介 # Python 人脸性别年龄检测系统介绍 简介 该系统基…

用idea搭建一个spring cloud微服务项目

以下是使用 IntelliJ IDEA 搭建 Spring Cloud 微服务项目的步骤&#xff1a; 创建一个新的 Maven 项目。 在 pom.xml 文件中添加以下依赖&#xff1a; <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-…

Android studio 迁移之后打开没反应

把Android studio由d盘迁移到c盘&#xff0c;点击没反应&#xff1b; 需要把C:\Users\xxxx\AppData\Roaming\Google\AndroidStudio2022.3 目录下的studio64.exe.vmoptions 修改为C:&#xff0c;删除该文件会导致无法安装app。 里面配置了一个

SpringMVC问题

文章目录 SpringMVC运行流程MVC的概念与请求在MVC中的执行路径&#xff0c;ResponsBody注解的用途SpringMVC启动流程 SpringMVC运行流程 • 客户端&#xff08;浏览器&#xff09;发送请求&#xff0c;直接请求到 DispatcherServlet 。 • DispatcherServlet 根据请求信息调用 …

SpringBoot问题

文章目录 Springboot特性 Springboot特性 自动装配&#xff1a;提供自动配置的“starter”项目对象模型&#xff08;POMS&#xff09;以简化Maven配置。比如使用 MongoDB 时&#xff0c;只需加入 MongoDB 的 Starter 包&#xff0c;然后配置 的连接信息&#xff0c;就可以直接使…

【React-Router】路由导航

1. 概念 路由系统中的多个路由之间需要进行路由跳转&#xff0c;并且在跳转的同时有可能需要传递参数进行通信。 2. 声明式导航 // /page/Login/index.jsimport { Link } from react-router-dom const Login () > {return <div>登录页{/* 解析成 a 链接 */}<Li…

php获取表单以POST方式或GET方式提交的值

在php中存在两个全局变量&#xff08;数组&#xff09;&#xff0c;其中$_GET数组用来记录表单通过GET方式提交的数据&#xff0c;$_POST数组用来记录表单通过POST方式提交的数据。 一、php获取GET方式提交的值 在php中通过以下代码来获取&#xff1a; $_GET[name] //nam…

Windows平台如何实现RTSP流二次编码并添加动态水印后推送RTMP或轻量级RTSP服务

技术背景 我们在对接RTSP播放器相关的技术诉求的时候&#xff0c;遇到这样的需求&#xff0c;客户做特种设备巡检的&#xff0c;需要把摄像头拍到的RTSP流拉下来&#xff0c;然后添加动态水印后&#xff0c;再生成新的RTSP URL&#xff0c;供平台调用。真个流程需要延迟尽可能…

Anthropic LLM论文阅读笔记

研究时间&#xff1a;与Instrcut GPT同期的工作&#xff0c;虽然其比ChatGPT发布更晚&#xff0c;但是其实完成的时间比ChatGPT更早。与ChatGPT的应用区别&#xff1a;该模型比ChatGPT回答我不知道的概率更高。将强化学习用于大语言模型&#xff08;RLHF&#xff09;&#xff1…

6.基于蜻蜓优化算法 (DA)优化的VMD参数(DA-VMD)

代码原理 基于蜻蜓优化算法 (Dragonfly Algorithm, DA) 优化的 VMD 参数&#xff08;DA-VMD&#xff09;是指使用蜻蜓优化算法对 VMD 方法中的参数进行自动调优和优化。 VMD&#xff08;Variational Mode Decomposition&#xff09;是一种信号分解方法&#xff0c;用于将复杂…

【数据结构】链表中二级指针的应用

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 (注:为方便演示本篇使用的x86系统,因此指针的大小为4个字节) 目录 &#x1f4cc;形参的改变不影响实参! 1.调用函数更改整型时传值调用与传址调用的区别 &#x1f38f;传值…

微服务学习|初识Docker、使用Docker、自定义镜像、DockerCompose、Docker镜像仓库

初识Docker 项目部署的问题 大型项目组件较多&#xff0c;运行环境也较为复杂&#xff0c;部署时会碰到一些问题 依赖关系复杂&#xff0c;容易出现兼容性问题 开发、测试、生产环境有差异 Docker如何解决依赖的兼容问题的? 将应用的Libs (函数库)、Deps (依赖)配置与应用…

线性回归的正则方法:岭回归和Lasso

线性回归的正则方法包括岭回归&#xff08;Ridge Regression&#xff09;和Lasso回归&#xff08;Least Absolute Shrinkage and Selection Operator Regression&#xff09;。这两种方法都是为了解决线性回归中可能存在的过拟合问题而提出的。 选择使用岭回归还是Lasso回归通常…

使用 goland 开发 golang 项目环境配置

方式1&#xff1a;使用 GOPATH 和 GOROOT 在 goland 中打开&#xff1a;Settings - Go&#xff0c;会看到 GOROOT、GOPATH&#xff0c;其相关解释与配置如下&#xff1a; GOROOT&#xff1a;对应 go 的安装路径&#xff0c;例如&#xff1a;D:\go\binGOPATH&#xff1a;是我们…

JavaScript中的事件循环 为什么是微任务先运行

无意中看到这个问题&#xff0c;以下是个人的看法 1、性能和响应性&#xff1a; 微任务通常比宏任务执行得更快&#xff0c;因为微任务通常涉及更少的工作量。将微任务放在宏任务之前可以尽早执行那些需要快速响应的任务&#xff0c;提高系统的响应性能。 2、Promise 的异步特…

3d标签云实现过程(tagcloud.js)同步原生和 vue

写在前面 本来是没有准备写这个知识点&#xff0c;但是下载这个 js 的时候发现很多都是要钱或者是积分的&#xff0c;我就不明白了一个开源了这么久的 js 怎么还有人拿来挣钱的&#xff0c;同时还有一些只有原生 html 的例子&#xff0c;但是现在都是 框架主导的一些项目&#…

【Exception】Error: Dynamic require of “path“ is not supported

Talk is cheap, show me the code. 环境 | Environment kversionOSwindows 11Node.jsv18.14.2npm9.5.0vite5.0.0vue3.3.8 报错日志 | Error log >npm run dev> app10.0.0 dev > viteERROR failed to load config from C:\code\frontend\app1\vite.config.js …

【LeetCode二叉树进阶题目】606,102,107

二叉树进阶题目 606. 根据二叉树创建字符串解题思路及实现 102. 二叉树的层序遍历解题思路及实现 107. 二叉树的层序遍历 II解题思路及实现 606. 根据二叉树创建字符串 描述 给你二叉树的根节点 root &#xff0c;请你采用前序遍历的方式&#xff0c;将二叉树转化为一个由括号…