如何获取Java堆的内存地址?

7
如何确定当前进程中运行的JVM的Java堆内存地址?也就是说,使用Java、C或其他调用获取一个void*指针或等效指针,指向JVM为堆分配的连续内存区域?
Matlab在其进程中嵌入了JVM。JVM分配的内存对于Matlab数组不可用,其中堆很重要,因为它占用了一个大的连续内存块并且永远不会缩小,而Matlab也需要连续内存块来存储其数组。如果在扩展期间重新分配堆,则可能导致碎片化。
我想对我的进程进行检测,以研究Java堆和Matlab内存视图之间的交互,并找出当其由于调整大小而移动时,并最好全部在进程内完成。这需要知道堆的地址。从java.lang.Runtime可以很容易地找到堆的大小,但无法找到其在内存中的地址。如何实现呢?
我在Windows XP和Server 2003上运行Sun的JRE 1.6.0_04,运行我们编写的代码,所以我们可以使用自定义Java、Matlab、JNI和C/C++代码。最好使用Java方法调用或JVM中支持的钩子,而不是低级别的hackery。
编辑:此目的是为了检查JVM的GC和Matlab的GC之间的交互。我不需要看到Java堆内部,也不会从该内存读取任何内容;我只想知道它在整个虚拟内存空间的上下文中的位置,Matlab的GC也试图将数据放入其中。

最终目标是分析Java/Matlab内存交互吗?还是为了访问相同的内存进行数据存储? - basszero
@basszero:仅分析Java/Matlab内存交互,不在该内存中读写。 - Andrew Janke
5个回答

2
一种快速获得JVM实际堆地址的方法是进入WinDbg,附加到JVM并发布单个!address命令。大约在0x2???????(这在不同版本的jvm上可能会有所不同,但对于该版本保持不变)附近将会有一个标记为PAGE_EXECUTE_READWRITE的大型VAD,这是进程内存中JVM的堆。
要确认,请在kernel32!VirtualAlloc上设置断点,并在JVM.DLL模块中进行初始化时,您会在调用VirtualAlloc时停顿,显示jvm分配其堆的位置。如果您检查此调用周围的代码,您可以看到如何计算地址。

抱歉我花了这么长时间才接受;一段时间内我被其他工作占据了。我之前不知道WinDbg,非常有用。 - Andrew Janke

2

稍微退后一步...您可以使用固定大小的Java堆吗?这样,关于重新分配和碎片化的问题就会消失。

在独立的Java调用中,需要指定类似于-Xmx500m和-Xms500m的内容来设置500Mb堆。您需要将其转换为Matlab所需的格式。


这可能有所帮助-但是Java堆分配的内存无法用于Matlab数组。在某些运行中,用户会执行Java堆密集型GUI操作,在其他情况下,则是进行M-code密集型数字操作。 固定大小的堆将以灵活性为代价解决碎片化问题。 这实际上是我想要这个工具的原因-通过问“由于堆重新分配而发生了多少碎片化”来查看固定堆的权衡是否值得。 - Andrew Janke

1

如果您只想让JVM堆缩小,可以尝试调整gc参数,例如-XX:MaxHeapFreeRatio(请参见http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp

我认为您无法通过JNI获取Java堆的指针。但是,Java堆只是Windows从进程堆之一分配给进程的内存。

你可以使用GetProcessHeaps函数(http://msdn.microsoft.com/en-us/library/aa366571(VS.85).aspx)从你的C++代码中获取进程堆,并使用HeapWalk函数开始遍历它们。在http://www.abstraction.net/content/articles/analyzing%20the%20heaps%20of%20a%20win32%20process.htm上有一个很好的例子。你可能能够通过寻找某些字节模式来发现哪些分配块被Java堆使用(JVM源代码可能会给你一些提示,但祝你好运理解!)


这听起来很有前途;我之前不知道有 GetProcessHeaps 这个函数。打算试着将其应用到我们的应用程序中。 - Andrew Janke
1
Java堆只是Windows从进程堆之一中为进程分配的内存。但事实并非如此,Java堆是通过kernel32!VirtualAlloc(在Windows上)分配的,因此不是来自进程堆之一。 - QAZ

0
GC可以在任何时候移动数据以压缩内存。因此,Java堆中的对象没有固定的指向。您可以使用ByteBuffer.allocateDirect()来分配"C"空间中的内存,而不是堆中的内存,它在内存中是固定的。

我不是在寻找Java堆中单个对象的位置,而是在寻找整个Java堆在进程的虚拟地址空间中的位置。 - Andrew Janke

0

我认为如果不使用定制的JVM,你想要的功能是无法实现的。理论上,你可以使用OpenJDK并进行补丁修复或启用一些跟踪功能(不确定是否有符合你需求的)。我认为像进程资源管理器这样的外部监控工具可以解决你的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接