国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - java 中Buffer源碼的分析

java 中Buffer源碼的分析

2020-11-12 17:23m1457285665 Java教程

這篇文章主要介紹了java 中Buffer源碼的分析的相關資料,需要的朋友可以參考下

javaBuffer源碼的分析

Buffer

Buffer的類圖如下:

java 中Buffer源碼的分析

除了Boolean,其他基本數據類型都有對應的Buffer,但是只有ByteBuffer才能和Channel交互。只有ByteBuffer才能產生Direct的buffer,其他數據類型的Buffer只能產生Heap類型的Buffer。ByteBuffer可以產生其他數據類型的視圖Buffer,如果ByteBuffer本身是Direct的,則產生的各視圖Buffer也是Direct的。

Direct和Heap類型Buffer的本質

首選說說JVM是怎么進行IO操作的。

JVM在需要通過操作系統調用完成IO操作,比如可以通過read系統調用完成文件的讀取。read的原型是:ssize_t read(int fd,void *buf,size_t nbytes),和其他的IO系統調用類似,一般需要緩沖區作為其中一個參數,該緩沖區要求是連續的。

Buffer分為Direct和Heap兩類,下面分別說明這兩類buffer。

Heap

Heap類型的Buffer存在于JVM的堆上,這部分內存的回收與整理和普通的對象一樣。Heap類型的Buffer對象都包含一個對應基本數據類型的數組屬性(比如:final **[] hb),數組才是Heap類型Buffer的底層緩沖區。

但是Heap類型的Buffer不能作為緩沖區參數直接進行系統調用,主要因為下面兩個原因。

  • JVM在GC時可能會移動緩沖區(復制-整理),緩沖區的地址不固定。
  • 系統調用時,緩沖區需要是連續的,但是數組可能不是連續的(JVM的實現沒要求連續)。

所以使用Heap類型的Buffer進行IO時,JVM需要產生一個臨時Direct類型的Buffer,然后進行數據復制,再使用臨時Direct的

Buffer作為參數進行操作系統調用。這造成很低的效率,主要是因為兩個原因:

  1. 需要把數據從Heap類型的Buffer里面復制到臨時創建的Direct的Buffer里面。
  2. 可能產生大量的Buffer對象,從而提高GC的頻率。所以在IO操作時,可以通過重復利用Buffer進行優化。

Direct

Direct類型的buffer,不存在于堆上,而是JVM通過malloc直接分配的一段連續的內存,這部分內存成為直接內存,JVM進行IO系統調用時使用的是直接內存作為緩沖區。

-XX:MaxDirectMemorySize,通過這個配置可以設置允許分配的最大直接內存的大小(MappedByteBuffer分配的內存不受此配置影響)。

直接內存的回收和堆內存的回收不同,如果直接內存使用不當,很容易造成OutOfMemoryError。Java沒有提供顯示的方法去主動釋放直接內存,sun.misc.Unsafe類可以進行直接的底層內存操作,通過該類可以主動釋放和管理直接內存。同理,也應該重復利用直接內存以提高效率。

MappedByteBuffer和DirectByteBuffer之間的關系

This is a little bit backwards: By rights MappedByteBuffer should be a subclass of DirectByteBuffer, but to keep the spec clear and simple, and for optimization purposes, it's easier to do it the other way around.This works because DirectByteBuffer is a package-private class.(本段話摘自MappedByteBuffer的源碼)

實際上,MappedByteBuffer屬于映射buffer(自己看看虛擬內存),但是DirectByteBuffer只是說明該部分內存是JVM在直接內存區分配的連續緩沖區,并不一是映射的。也就是說MappedByteBuffer應該是DirectByteBuffer的子類,但是為了方便和優化,把MappedByteBuffer作為了DirectByteBuffer的父類。另外,雖然MappedByteBuffer在邏輯上應該是DirectByteBuffer的子類,而且MappedByteBuffer的內存的GC和直接內存的GC類似(和堆GC不同),但是分配的MappedByteBuffer的大小不受-XX:MaxDirectMemorySize參數影響。

MappedByteBuffer封裝的是內存映射文件操作,也就是只能進行文件IO操作。MappedByteBuffer是根據mmap產生的映射緩沖區,這部分緩沖區被映射到對應的文件頁上,屬于直接內存在用戶態,通過MappedByteBuffer可以直接操作映射緩沖區,而這部分緩沖區又被映射到文件頁上,操作系統通過對應內存頁的調入和調出完成文件的寫入和寫出。

MappedByteBuffer

通過FileChannel.map(MapMode mode,long position, long size)得到MappedByteBuffer,下面結合源碼說明MappedByteBuffer的產生過程。

FileChannel.map的源碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
public MappedByteBuffer map(MapMode mode, long position, long size)
    throws IOException
  {
    ensureOpen();
    if (position < 0L)
      throw new IllegalArgumentException("Negative position");
    if (size < 0L)
      throw new IllegalArgumentException("Negative size");
    if (position + size < 0)
      throw new IllegalArgumentException("Position + size overflow");
    //最大2G
    if (size > Integer.MAX_VALUE)
      throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE");
    int imode = -1;
    if (mode == MapMode.READ_ONLY)
      imode = MAP_RO;
    else if (mode == MapMode.READ_WRITE)
      imode = MAP_RW;
    else if (mode == MapMode.PRIVATE)
      imode = MAP_PV;
    assert (imode >= 0);
    if ((mode != MapMode.READ_ONLY) && !writable)
      throw new NonWritableChannelException();
    if (!readable)
      throw new NonReadableChannelException();
 
    long addr = -1;
    int ti = -1;
    try {
      begin();
      ti = threads.add();
      if (!isOpen())
        return null;
      //size()返回實際的文件大小
      //如果實際文件大小不符合,則增大文件的大小,文件的大小被改變,文件增大的部分默認設置為0。
      if (size() < position + size) { // Extend file size
        if (!writable) {
          throw new IOException("Channel not open for writing " +
            "- cannot extend file to required size");
        }
        int rv;
        do {
          //增大文件的大小
          rv = nd.truncate(fd, position + size);
        } while ((rv == IOStatus.INTERRUPTED) && isOpen());
      }
      //如果要求映射的文件大小為0,則不調用操作系統的mmap調用,只是生成一個空間容量為0的DirectByteBuffer
      //并返回
      if (size == 0) {
        addr = 0;
        // a valid file descriptor is not required
        FileDescriptor dummy = new FileDescriptor();
        if ((!writable) || (imode == MAP_RO))
          return Util.newMappedByteBufferR(0, 0, dummy, null);
        else
          return Util.newMappedByteBuffer(0, 0, dummy, null);
      }
      //allocationGranularity的大小在我的系統上是4K
      //頁對齊,pagePosition為第多少頁
      int pagePosition = (int)(position % allocationGranularity);
      //從頁的最開始映射
      long mapPosition = position - pagePosition;
      //因為從頁的最開始映射,增大映射空間
      long mapSize = size + pagePosition;
      try {
        // If no exception was thrown from map0, the address is valid
        //native方法,源代碼在openjdk/jdk/src/solaris/native/sun/nio/ch/FileChannelImpl.c,
        //參見下面的說明
        addr = map0(imode, mapPosition, mapSize);
      } catch (OutOfMemoryError x) {
        // An OutOfMemoryError may indicate that we've exhausted memory
        // so force gc and re-attempt map
        System.gc();
        try {
          Thread.sleep(100);
        } catch (InterruptedException y) {
          Thread.currentThread().interrupt();
        }
        try {
          addr = map0(imode, mapPosition, mapSize);
        } catch (OutOfMemoryError y) {
          // After a second OOME, fail
          throw new IOException("Map failed", y);
        }
      }
 
      // On Windows, and potentially other platforms, we need an open
      // file descriptor for some mapping operations.
      FileDescriptor mfd;
      try {
        mfd = nd.duplicateForMapping(fd);
      } catch (IOException ioe) {
        unmap0(addr, mapSize);
        throw ioe;
      }
 
      assert (IOStatus.checkAll(addr));
      assert (addr % allocationGranularity == 0);
      int isize = (int)size;
      Unmapper um = new Unmapper(addr, mapSize, isize, mfd);
      if ((!writable) || (imode == MAP_RO)) {
        return Util.newMappedByteBufferR(isize,
                         addr + pagePosition,
                         mfd,
                         um);
      } else {
        return Util.newMappedByteBuffer(isize,
                        addr + pagePosition,
                        mfd,
                        um);
      }
    } finally {
      threads.remove(ti);
      end(IOStatus.checkAll(addr));
    }
  }

map0的源碼實現:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileChannelImpl_map0(JNIEnv *env, jobject this,
                   jint prot, jlong off, jlong len)
{
  void *mapAddress = 0;
  jobject fdo = (*env)->GetObjectField(env, this, chan_fd);
  //linux系統調用是通過整型的文件id引用文件的,這里得到文件id
  jint fd = fdval(env, fdo);
  int protections = 0;
  int flags = 0;
 
  if (prot == sun_nio_ch_FileChannelImpl_MAP_RO) {
    protections = PROT_READ;
    flags = MAP_SHARED;
  } else if (prot == sun_nio_ch_FileChannelImpl_MAP_RW) {
    protections = PROT_WRITE | PROT_READ;
    flags = MAP_SHARED;
  } else if (prot == sun_nio_ch_FileChannelImpl_MAP_PV) {
    protections = PROT_WRITE | PROT_READ;
    flags = MAP_PRIVATE;
  }
  //這里就是操作系統調用了,mmap64是宏定義,實際最后調用的是mmap
  mapAddress = mmap64(
    0,          /* Let OS decide location */
    len,         /* Number of bytes to map */
    protections,     /* File permissions */
    flags,        /* Changes are shared */
    fd,          /* File descriptor of mapped file */
    off);         /* Offset into file */
 
  if (mapAddress == MAP_FAILED) {
    if (errno == ENOMEM) {
      //如果沒有映射成功,直接拋出OutOfMemoryError
      JNU_ThrowOutOfMemoryError(env, "Map failed");
      return IOS_THROWN;
    }
    return handle(env, -1, "Map failed");
  }
 
  return ((jlong) (unsigned long) mapAddress);
}

雖然FileChannel.map()的zise參數是long,但是size的大小最大為Integer.MAX_VALUE,也就是最大只能映射最大2G大小的空間。實際上操作系統提供的MMAP可以分配更大的空間,但是JAVA限制在2G,ByteBuffer等Buffer也最大只能分配2G大小的緩沖區。

MappedByteBuffer是通過mmap產生得到的緩沖區,這部分緩沖區是由操作系統直接創建和管理的,最后JVM通過unmmap讓操作系統直接釋放這部分內存。

Haep****Buffer

下面以ByteBuffer為例,說明Heap類型Buffer的細節。

該類型的Buffer可以通過下面方式產生:

  • ByteBuffer.allocate(int capacity)
  • ByteBuffer.wrap(byte[] array)      使用傳入的數組作為底層緩沖區,變更數組會影響緩沖區,變更緩沖區也會影響數組。
  • ByteBuffer.wrap(byte[] array,int offset, int length)

使用傳入的數組的一部分作為底層緩沖區,變更數組的對應部分會影響緩沖區,變更緩沖區也會影響數組。

DirectByteBuffer

DirectByteBuffer只能通過ByteBuffer.allocateDirect(int capacity) 產生。

ByteBuffer.allocateDirect()源碼如下:

?
1
2
3
public static ByteBuffer allocateDirect(int capacity) {
   return new DirectByteBuffer(capacity);
 }

DirectByteBuffer()源碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
DirectByteBuffer(int cap) {          // package-private
 
  super(-1, 0, cap, cap);
  //直接內存是否要頁對齊,我本機測試的不用
  boolean pa = VM.isDirectMemoryPageAligned();
  //頁的大小,本機測試的是4K
  int ps = Bits.pageSize();
  //如果頁對齊,則size的大小是ps+cap,ps是一頁,cap也是從新的一頁開始,也就是頁對齊了
  long size = Math.max(1L, (long)cap + (pa ? ps : 0));
  //JVM維護所有直接內存的大小,如果已分配的直接內存加上本次要分配的大小超過允許分配的直接內存的最大值會
  //引起GC,否則允許分配并把已分配的直接內存總量加上本次分配的大小。如果GC之后,還是超過所允許的最大值,
  //則throw new OutOfMemoryError("Direct buffer memory");
  Bits.reserveMemory(size, cap);
 
  long base = 0;
  try {
    //是吧,unsafe可以直接操作底層內存
    base = unsafe.allocateMemory(size);
  } catch (OutOfMemoryError x) {、
    //沒有分配成功,把剛剛加上的已分配的直接內存的大小減去。
    Bits.unreserveMemory(size, cap);
    throw x;
  }
  unsafe.setMemory(base, size, (byte) 0);
  if (pa && (base % ps != 0)) {
    // Round up to page boundary
    address = base + ps - (base & (ps - 1));
  } else {
    address = base;
  }
  cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
  att = null;
}

unsafe.allocateMemory()的源碼在openjdk/src/openjdk/hotspot/src/share/vm/prims/unsafe.cpp中。具體的源碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
UNSAFE_ENTRY(jlong, Unsafe_AllocateMemory(JNIEnv *env, jobject unsafe, jlong size))
 UnsafeWrapper("Unsafe_AllocateMemory");
 size_t sz = (size_t)size;
 if (sz != (julong)size || size < 0) {
  THROW_0(vmSymbols::java_lang_IllegalArgumentException());
 }
 if (sz == 0) {
  return 0;
 }
 sz = round_to(sz, HeapWordSize);
 //最后調用的是 u_char* ptr = (u_char*)::malloc(size + space_before + space_after),也就是malloc。
 void* x = os::malloc(sz, mtInternal);
 if (x == NULL) {
  THROW_0(vmSymbols::java_lang_OutOfMemoryError());
 }
 //Copy::fill_to_words((HeapWord*)x, sz / HeapWordSize);
 return addr_to_java(x);
UNSAFE_END

JVM通過malloc分配得到連續的緩沖區,這部分緩沖區可以直接作為緩沖區參數進行操作系統調用。

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

原文鏈接:http://blog.csdn.net/m1457285665/article/details/72911004

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 狠狠影院 | 欧洲精品久久久久毛片完整版 | 龙珠z普通话国语版在线观看 | 国产黄网| 国产福利在线视频 | 免费观看国产精品 | 国产特级毛片aaaaaa高清 | 伊人春色网 | 国产免费性 | 欧美日韩精品电影 | 综州合另类 | 成人亚洲 | 一本色道久久综合狠狠躁篇怎么玩 | 国产精品久久久久久av公交车 | 日韩精品专区 | 国产一区久久久 | 亚洲精品字幕 | 中文字幕影院 | 动漫精品一区二区 | 成人免费大片黄在线播放 | 日韩三级 | 亚洲国产精品电影在线观看 | 九色av | 黄色在线免费看 | 国产一区二区三区免费看 | 亚洲视频三区 | а√在线中文在线新版 | 人和拘一级毛片 | 国产精品视频免费 | 精品视频在线观看 | 久久亚洲一区 | 国产精品久久精品 | 看av片| 欧美久| 日韩av高清 | 欧美激情精品久久久久久 | 久久亚洲欧美日韩精品专区 | 成人在线免费小视频 | 日韩一二| 在线无码| 欧美精品福利视频 |