Heim >Java >javaLernprogramm >Analysieren Sie den Buffer-Quellcode in Java
In diesem Artikel werden hauptsächlich relevante Informationen zur Analyse des Pufferquellcodes in Java vorgestellt. Freunde, die sie benötigen, können sich auf
Analyse des Pufferquellcodes in Java
Buffer
Das Klassendiagramm von Buffer lautet wie folgt: Zusätzlich zu Boolean gibt es auch andere grundlegendeDas Wesen von Puffern vom Typ „Direkt“ und „Heap“
Die erste Wahl besteht darin, darüber zu sprechen, wie die JVM E/A-Vorgänge ausführt. JVM muss E/A-Vorgänge über Betriebssystemaufrufe abschließen. Beispielsweise kann das Lesen von Dateien über Lesesystemaufrufe abgeschlossen werden. Der Prototyp von read lautet:ssize_t read(int fd, void *buf, size_t nbytes), ähnlich wie bei anderen E/A-Systemaufrufen, erfordert im Allgemeinen einen Puffer als einen der Parameter, und der Puffer muss vorhanden sein kontinuierlich.
Puffer ist in zwei Kategorien unterteilt: Direkt und Heap. Diese beiden Arten von Puffern werden im Folgenden erläutert.Heap
Auf dem JVM-Heap ist ein Puffer vom Typ Heap vorhanden. Das Recycling und die Anordnung dieses Teils des Speichers sind die gleichen wie bei gewöhnlichen Objekten. Pufferobjekte vom Heap-Typ enthalten alle ein Array-Attribut, das einem Basisdatentyp entspricht (z. B. final **[] hb), und das Array ist der zugrunde liegende Puffer des Heap-Typs Buffer.des temporären Direct verwenden
Puffer wird als Parameter für Betriebssystemaufrufe verwendet. Dies führt hauptsächlich aus zwei Gründen zu einer sehr geringen Effizienz:Direkt
Der Puffer vom Typ „Direkt“ existiert nicht auf dem Heap, sondern ist ein kontinuierliches Segment, das direkt von der JVM über malloc zugewiesen wird .Speicher, dieser Teil des Speichers wird zum Direktspeicher, und die JVM verwendet den Direktspeicher als Puffer, wenn sie E/A-Systemaufrufe durchführt.Beziehung zwischen MappedByteBuffer und DirectByteBuffer
Das ist ein bisschen rückwärts: Eigentlich sollte MappedByteBuffer eine Unterklasse von DirectByteBuffer sein, aber um die Spezifikation klar und einfach zu halten, und Aus Optimierungsgründen ist es einfacher, es umgekehrt zu machen, da DirectByteBuffer eine paketprivate Klasse ist (Dieser Absatz stammt aus dem Quellcode von MappedByteBuffer)MappedByteBuffer
MappedByteBuffer über FileChannel.map abrufen (MapMode-Modus, lange Position, lange Größe) Der Generierungsprozess von MappedByteBuffer wird unten anhand des Quellcodes erläutert.
Quellcode von FileChannel.map:
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)); } }
Quellcode-Implementierung von map0:
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); }
Obwohl der Zise-Parameter von FileChannel.map() lang ist, beträgt die maximale Größe Integer.MAX_VALUE, was bedeutet, dass nur die maximale Größe des Speicherplatzes möglich ist auf 2G abgebildet werden. Tatsächlich kann der vom Betriebssystem bereitgestellte MMAP größeren Speicherplatz zuweisen, JAVA ist jedoch auf 2G beschränkt, und Puffer wie ByteBuffer können nur eine maximale Puffergröße von 2G zuweisen.
MappedByteBuffer ist ein durch mmap generierter Puffer. Dieser Teil des Puffers wird direkt vom Betriebssystem erstellt und verwaltet. Schließlich ermöglicht die JVM dem Betriebssystem, diesen Teil des Speichers direkt durch unmmap freizugeben .
Haep****Buffer
Im Folgenden wird ByteBuffer als Beispiel verwendet, um die Details des Puffers vom Heap-Typ zu veranschaulichen.
Diese Art von Puffer kann auf folgende Weise generiert werden:
ByteBuffer.allocate(int Capacity)
ByteBuffer.wrap(byte[] array) verwendet das eingehende Array als zugrunde liegenden Puffer. Eine Änderung des Arrays wirkt sich auf den Puffer aus, und eine Änderung des Puffers wirkt sich auch auf das Array aus.
ByteBuffer.wrap(byte[] array, int offset, int length)
Teil des übergebenen Arrays als zugrunde liegenden Puffer verwenden, Das Ändern des entsprechenden Teils des Arrays wirkt sich auf den Puffer aus, und das Ändern des Puffers wirkt sich auch auf das Array aus.
DirectByteBuffer
DirectByteBuffer kann nur von ByteBuffer.allocateDirect(int Capacity) generiert werden.
Der Quellcode von ByteBuffer.allocateDirect() lautet wie folgt:
public static ByteBuffer allocateDirect(int capacity) { return new DirectByteBuffer(capacity); }
Der Quellcode von DirectByteBuffer() lautet wie folgt:
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; }
Der Quellcode von unsafe.allocateMemory() befindet sich in openjdk/src/openjdk/hotspot/src/share/vm/prims/unsafe.cpp. Der spezifische Quellcode lautet wie folgt:
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
Die JVM weist über malloc einen kontinuierlichen Puffer zu. Dieser Teil des Puffers kann direkt als Pufferparameter für den Betrieb verwendet werden Systemaufrufe.
Das obige ist der detaillierte Inhalt vonAnalysieren Sie den Buffer-Quellcode in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!