什么是本机内存:
即进程内存。本机内存是可用于运行时进程的内存,它与 Java 应用程序使用的 java 堆内存不同。每种虚拟化资源(包括 Java 堆和 Java 线程)都必须
存储在本机内存中,虚拟机在运行时使用的数据也是如此。
里面存储哪些内容:
Java 堆:使用的本机内存大小保持不变,而且由 -Xmx 值(最大堆大小)指定。
垃圾收集:对于维护 Java 堆的内存管理系统,需要更多本机内存来维护它的状态。当进行垃圾收集时,必须分配数据结构来跟踪空闲存储空间和记录进度。这些数据结构的确切大小和性质因实现的不同而不同,但许多数据结构都与堆大小成正比。
即时 (JIT) 编译器:字节码编译使用本机内存(使用方式与 gcc 等静态编译器使用内存来运行一样),但 JIT 编译器的输入(字节码)和输出(可执行代码)必须也存储在本机内存中。包含多个经过 JIT 编译的方法的 Java 应用程序会使用比小型应用程序更多的本机内存。
类和类加载器:Java 应用程序由一些类组成,这些类定义对象结构和方法逻辑。Java 应用程序也使用 Java 运行时类库(比如 java.lang.String)中的类,也可以使用第三方库。这些类需要存储在内存中以备使用。
存储类的方式取决于具体实现。Sun JDK 使用永久生成(permanent generation,PermGen)堆区域。
Java 5 的 IBM 实现会为每个类加载器分配本机内存块,并将类数据存储在其中。
在运行时生成类
许多 JEE 应用程序使用 JavaServer Pages (JSP) 技术来生成 Web 页面。使用 JSP 会为执行的每个 .jsp 页面生成一个类,并且这些类会在加载它们的类加载器的整个生存期中一直存在 —— 这个生存期通常是 Web 应用程序的生存期。
使用 Java 反射:
Java 运行时必须将一个反射对象(比如 java.lang.reflect.Field)的方法连接到被反射到的对象或类。这可以通过使用 Java 本机接口(Java Native Interface,JNI)访问器来完成,这种方法需要的设置很少,但是速度缓慢。
在运行时为您想要反射到的每种对象类型动态构建一个类。后一种方法在设置上更慢,但运行速度更快,非常适合于经常反射到一个特定类的应用程序。
Java 运行时在最初几次反射到一个类时使用 JNI 方法,但当使用了若干次 JNI 方法之后,访问器会膨胀为字节码访问器,这涉及到构建类并通过新的类加载器进行加载。执行多次反射可能导致创建了许多访问器类和类加载器。保持对反射对象的引用会导致这些类一直存活,并继续占用空间。因为创建字节码访问器非常缓慢,所以 Java 运行时可以缓存这些访问器以备以后使用。一些应用程序和框架还会缓存反射对象,这进一步增加了它们的本机内存占用。
JNI
JNI 支持本机代码(使用 C 和 C++ 等本机编译语言编写的应用程序)调用 Java 方法,反之亦然。Java 运行时本身极大地依赖于 JNI 代码来实现类库功能,比如文件和网络 I/O。
JNI 应用程序可能通过 3 种方式增加 Java 运行时的本机内存占用:
JNI 应用程序的本机代码被编译到共享库中,或编译为加载到进程地址空间中的可执行文件。大型本机应用程序可能仅仅加载就会占用大量进程地址空间。
本机代码必须与 Java 运行时共享地址空间。任何本机代码分配或本机代码执行的内存映射都会耗用 Java 运行时的内存。
某些 JNI 函数可能在它们的常规操作中使用本机内存。GetTypeArrayElements 和 GetTypeArrayRegion 函数可以将 Java 堆数据复制到本机内存缓冲区中,以供本机代码使用。是否复制数据依赖于运行时实现。(IBM Developer Kit for Java 5.0 和更高版本会进行本机复制)。通过这种方式访问大量 Java 堆数据可能会使用大量本机堆。
NIO
Java 1.4 中添加的新 I/O (NIO) 类引入了一种基于通道和缓冲区来执行 I/O 的新方式。就像 Java 堆上的内存支持 I/O 缓冲区一样,NIO 添加了对直接 ByteBuffer 的支持(使用 java.nio.ByteBuffer.allocateDirect() 方法进行分配), ByteBuffer 受本机内存而不是 Java 堆支持。直接 ByteBuffer 可以直接传递到本机操作系统库函数,以执行 I/O — 这使这些函数在一些场景中要快得多,因为它们可以避免在 Java 堆与本机堆之间复制数据。
对于在何处存储直接 ByteBuffer 数据,很容易产生混淆。应用程序仍然在 Java 堆上使用一个对象来编排 I/O 操作,但持有该数据的缓冲区将保存在本机内存中,Java 堆对象仅包含对本机堆缓冲区的引用。非直接 ByteBuffer 将其数据保存在 Java 堆上的 byte[] 数组中。
线程
应用程序中的每个线程都需要内存来存储器堆栈(用于在调用函数时持有局部变量并维护状态的内存区域)。每个 Java 线程都需要堆栈空间来运行。根据实现的不同,Java
线程可以分为本机线程和 Java 堆栈。除了堆栈空间,每个线程还需要为线程本地存储(thread-local storage)和内部数据结构提供一些本机内存。
堆栈大小因 Java 实现和架构的不同而不同。一些实现支持为 Java 线程指定堆栈大小,其范围通常在 256KB 到 756KB 之间。
如何回收
Java 运行时可以卸载类来回收空间,但是只有在非常严酷的条件下才会这样做。不能卸载单个类,而是卸载类加载器,随其加载的所有类都会被卸载。只有在以下情况下才能
卸载类加载器:
Java 堆不包含对表示该类加载器的 java.lang.ClassLoader 对象的引用。
Java 堆不包含对表示类加载器加载的类的任何 java.lang.Class 对象的引用。
在 Java 堆上,该类加载器加载的任何类的所有对象都不再存活(被引用)。
Java 运行时为所有 Java 应用程序创建的 3 个默认类加载器( bootstrap、extension 和 application )都不可能满足这些条件,因此,任何系统类(比如 java.lang.String)或通过应用程序类加载器加载的任何应用程序类都不能在运行时释放。
线程占用内存的回收或高效利用:使线程利用率较高,减少空闲线程。
回收时机:
运行时也只会将收集类加载器作为 GC 周期的一部分。一些实现只会在某些 GC 周期中卸载类加载器。
NIO 直接 ByteBuffer 对象会自动清理本机缓冲区,但这个过程只能作为 Java 堆 GC 的一部分来执行。
如何配置
配置堆栈大小
配置类加载大小
配置线程堆栈大小
可重复使用的结构:
NIO、类加载器、线程
依赖于GC回收的部件:
NIO、类加载器
不可回收,必须保证的的部分:
JIT、JNI,类加载器在大部分情况也很难回收。
如何搭配使用:
对可控部分的合理配置,保证不可控部分有必要的内存。
合理控制堆大小,以促使GC发生,高效利用可重复使用的部分。
分享到:
相关推荐
我们也可以使用PerformanceCounters来监视系统的性能,包括CPU使用率、内存使用率和磁盘使用率等。 在系统内存方面,我们可以使用GlobalMemoryStatus函数来获得系统的内存信息,包括物理内存的大小、可用内存的大小...
标题“Java加载dll,导致Java进程内存泄露”涉及到的是Java平台与本地库(DLL)交互时可能出现的问题。在Java中,通过Java Native Interface (JNI) 可以调用C/C++编写的动态链接库(DLL),实现Java代码与本地代码的...
java进程间通讯机制代码----RMI、共享内存、Socket、管道,等方式,每种方法我都讲了原理和例子程序,很有参考意义。在网上很难找到的。
其次,让我们来分析 HEAP 的使用情况。根据 jmap 的执行结果,我们可以看到,Young Generation 的 Eden Space 使用了 89.98% 的内存,从空间使用了 81.63% 的内存,To Space 没有使用任何内存。Old Generation 使用...
在Java中查看内存使用情况和启动新进程是常见的需求,特别是在性能优化和问题排查时。在本篇文章中,我们将深入探讨如何使用Sigar库来实现这些功能。 Sigar(System Information Gatherer and Reporter)是一个跨...
- **调试技巧**:可以通过工具如VisualVM或JConsole来监控JVM的内存使用情况,包括本机内存的使用状态。 - **解决方案**:针对不同的问题,可以采取相应的解决措施,如增加堆外内存大小、减少线程数量等。 #### 五...
JAVA进程占用高内存缘由分析与优化方法 在 Java 进程中,高内存占用是一个常见的问题,本文将通过 jmap 和 ps 命令来分析 Java 进程的内存占用情况,并讨论可能的优化方法。 1. Java 进程的内存占用分析 使用 ...
通过使用这两个类,可以获取系统的内存使用情况、物理内存大小、操作系统版本、线程总数等信息。例如: ```java import java.io.*; import com.sun.management.OperatingSystemMXBean; import sun.management....
实现Java进程间通信的关键在于,不同进程将同一物理文件的不同部分映射到各自的内存空间,通过修改映射的内存内容,可以实现数据的交换。由于MappedByteBuffer与文件内容同步,当一个进程写入数据后,另一个进程可以...
除了MAT,还有一些其他工具和策略可以帮助检测内存泄露,如使用VisualVM、JProfiler等,或者通过JMX监控JVM的内存使用情况。在代码层面,可以使用弱引用、软引用等技术避免不必要的内存占用。 对于内存溢出问题,...
【标题】:深入理解Java内存使用与优化:从代码到Java堆 【描述】:本文旨在帮助Java开发者深入了解从编写代码到Java堆的内存管理过程,以便更好地优化应用程序的内存使用。通过分析Java代码中的内存开销,以及讨论...
Sigar是一个跨平台的系统监视接口,它提供了统一的API来获取操作系统相关的各种信息,包括但不限于CPU负载、内存使用、进程管理等。Sigar支持Windows、Linux等多种操作系统,是进行系统级监控的理想选择之一。 ####...
Java编程语言在处理大型应用程序时,内存管理是一个至关重要的环节。内存泄漏是导致程序性能下降,甚至引发Out of...确保定期监控和分析内存使用情况,可以预防和早期发现潜在的内存问题,从而保持应用的高效稳定运行。
Java中的`java.lang.management`包提供了用于监控和管理Java虚拟机(JVM)的工具,可以获取关于内存使用情况的详细信息,这对于分析和调试内存问题非常有帮助。你可以使用`MemoryMXBean`、`GarbageCollectorMXBean`...
在Linux系统中,监控Java进程及其线程的CPU使用情况是进行性能调优的重要环节。以下是一些关于如何实现这一目标的关键知识点。 1. **`ps` 命令**: - `ps` 是一个用于报告当前系统中进程状态的命令。基本用法如 `...
本压缩包“进程的内存监视.rar”可能包含一系列关于如何监测和理解操作系统中进程内存使用情况的资料。 内存监视可以帮助我们了解程序运行时的内存占用,包括堆、栈空间,以及虚拟内存的使用状况。这对于优化程序...
Java模拟操作系统是一个基于Java编程语言实现的微型操作系统模型,它主要涵盖了四个核心领域:内存管理、进程管理、文件管理和进程通信。这样的项目旨在帮助开发者理解操作系统的工作原理,并提供了一个实践平台来...