从 Java9 共享内存加载 modules 说起 - JAVA - 编程开发
设为首页 加入收藏

TOP

从 Java9 共享内存加载 modules 说起(一)
2018-03-05 08:43:32 】 浏览:205
Tags:Java9 共享 内存 加载 modules 说起

Jdk9后加载lib/modules的方式

从jdk的代码里可以看出来,默认的实现加载lib/modules是用mmap来加载的。

class NativeImageBuffer {
    static {
        java.security.AccessController.doPrivileged(
                new java.security.PrivilegedAction<Void>() {
                    public Void run() {
                        System.loadLibrary("jimage");
                        return null;
                    }
                });
    }

    native static ByteBuffer getNativeMap(String imagePath);
}

在jimage动态库里最终是一个cpp实现的ImageFileReader来读取的。它在64位os上使用的是mmap方式:

https://github.com/dmlloyd/openjdk/blob/jdk/jdk10/src/java.base/share/native/libjimage/imageFile.cpp#L44

启动多个jvm时会有好处:

  • 减少内存占用
  • 加快启动速度

突然有个想法,怎么验证多个jvm的确共享了内存?

下面来验证一下,思路是:

  1. 先获取进程的mmap信息
  2. 获取jvm进程映射modules的虚拟地址
  3. 从虚拟地址转换为物理地址
  4. 启动两个jvm进程,计算它们映射modules是否物理地址是一样的

linux下查看进程的mmap信息

  1. 使用pmap -x $pid命令
  2. 直接查看 cat /proc/$pid/maps文件的内容

启动一个jshell之后,用pmap查看mmap信息,其中RSS(resident set size)列表示真实占用的内存。:

$ pmap -x 24615
24615:   jdk9/jdk-9.0.4/bin/jshell
Address           Kbytes     RSS   Dirty Mode  Mapping
0000000000400000       4       4       0 r-x-- jshell
0000000000601000       4       4       4 rw--- jshell
000000000111b000     132     120     120 rw---   [ anon ]
...
00007f764192c000      88      64       0 r-x-- libnet.so
00007f7641942000    2048       0       0 ----- libnet.so
00007f7641b42000       4       4       4 rw--- libnet.so
00007f7641b43000    2496     588     588 rwx--   [ anon ]
...
00007f7650b43000  185076    9880       0 r--s- modules
00007f765c000000    5172    5124    5124 rw---   [ anon ]

---------------- ------- ------- -------
total kB         2554068  128756  106560

我们可以找到modules文件的信息:

00007f7650b43000  185076    9880       0 r--s- modules

它的文件映射大小是185076kb,实际使用内存大小是9880kb。

linux kernel关于pagemap的说明

上面我们获取到了modules的虚拟地址,但是还需要转换为物理地址。

正常来说一个进程是没有办法知道它自己的虚拟地址对应的是什么物理地址。不过我们用linux kernel提供的信息可以读取,转换为物理地址。

linux每个进程都有个/proc/$pid/pagemap文件,里面记录了内存页的信息:

https://www.kernel.org/doc/Documentation/vm/pagemap.txt

简而言之,在pagemap里每一个virtual page都有一个对应的64 bit的信息:

    * Bits 0-54  page frame number (PFN) if present
    * Bits 0-4   swap type if swapped
    * Bits 5-54  swap offset if swapped
    * Bit  55    pte is soft-dirty (see Documentation/vm/soft-dirty.txt)
    * Bit  56    page exclusively mapped (since 4.2)
    * Bits 57-60 zero
    * Bit  61    page is file-page or shared-anon (since 3.5)
    * Bit  62    page swapped
    * Bit  63    page present

只要把虚拟地址转换为pagemap文件里的offset,就可以读取具体的virtual page信息。计算方法是:

// getpagesize()是系统调用
// 64bit是8字节
long virtualPageIndex = virtualAddress / getpagesize()
offset = virtualPageIndex * 8

从offset里读取出来的64bit里,可以获取到page frame number,如果想要得到真正的物理地址,还需要再转换:

// pageFrameNumber * getpagesize() 获取page的开始地址
// virtualAddress % getpagesize() 获取到page里的偏移地址
long pageFrameNumber = // read from pagemap file
physicalAddress = pageFrameNumber * getpagesize() + virtualAddress % getpagesize();

虚拟地址转换物理地址的代码

参考这里的代码:https://github.com/cirosantilli/linux-kernel-module-cheat/blob/master/kernel_module/user/common.h

得到的一个从虚拟地址转换为物理地址的代码:

#define _POSIX_C_SOURCE 200809L
#include <fcntl.h> /* open */
#include <stdint.h> /* uint64_t  */
#include <stdlib.h> /* size_t */
#include <unistd.h> /* pread, sysconf */

int BUFSIZ = 1024;

typedef struct {
    uint64_t pfn : 54;
    unsigned int soft_dirty : 1;
    unsigned int file_page : 1;
    unsigned int swapped : 1;
    unsigned int present : 1;
} PagemapEntry;

/* Parse the pagemap entry for the given virtual address.
 *
 * @param[out] entry      the parsed entry
 * @param[in]  pagemap_
编程开发网
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇侦探剧场:堆内存神秘溢出事件 下一篇Dockerfile : tomcat 镜像编写

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

最新文章

热门文章

C 语言

C++基础

windows编程基础

linux编程基础

C/C++面试题目