This is the 19th day of my participation in the Genwen Challenge
Out of memory
Off-heap memory is memory that is not controlled by the JVM. In simple terms, after stack memory, all that is left is out-of-heap memory (from a Java runtime memory perspective, of course), which is managed directly by the operating system, not the virtual machine. And the reason for using off-heap memory,
There are several advantages over in-heap memory:
- – Reduced garbage collection work because garbage collection suspends other work (possibly using multiple threads or time slices, not even noticeable)
Out-of-heap memory is managed directly by the operating system, not the JVM, so using it keeps in-heap memory smaller and reduces the impact of garbage collection on program performance.
- Is to improve the efficiency of IO operations! If you write data from the heap to disk, the data will be copied to out-of-heap memory, the kernel buffer, and then written to disk by the OS. Using out-of-heap memory can avoid this copying operation.
The memory in the heap is actually the [process buffer] of the user process, belonging to the user mode; Off-heap memory is managed by the operating system (kernel buffer) and belongs to the kernel state.
Nature also has a downside:
-
Out-of-heap memory is hard to control, and if it leaks, it can be difficult to troubleshoot
-
Off-heap memory is relatively unsuitable for storing very complex objects. Generally simple objects or flat objects are suitable.
-
Since it is the memory mechanism of the operating system, it needs to be allocated by local method, which is complicated and slow
Direct memory usage
-
Out-of-heap memory is created by java.nio’s ByteBuffer, which is requested by calling the allocateDirect method.
-
You can control the size of off-heap memory by setting -xx :MaxDirectMemorySize=10M.
Garbage collection of off-heap memory
-
Since out-of-heap memory is not directly controlled by the JVM, garbage collection can only happen during full GC! Full GC, which occurs when the garbage is collected and the System. GC is called, is definitely not enough for our needs!
-
Manual control to reclaim out of heap memory! Sun.nio is an internal implementation of Java.nio.
package xing.test;
import java.nio.ByteBuffer;
import sun.nio.ch.DirectBuffer;
public class NonHeapTest {
public static void clean(final ByteBuffer byteBuffer) {
if(byteBuffer.isDirect()) { ((DirectBuffer)byteBuffer).cleaner().clean(); }}public static void sleep(long i) {
try {
Thread.sleep(i);
}catch(Exception e) {
/*skip*/}}public static void main(String []args) throws Exception {
ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024 * 200);
System.out.println("start");
sleep(5000);
clean(buffer);// Perform garbage collection
// System.gc(); // Perform Full GC for garbage collection
System.out.println("end");
sleep(5000); }}Copy the code
Zero copy
-
When a user process needs to write data to disk, it copies the contents of memory in the user buffer (process buffer) heap to the kernel buffer (out-of-heap memory), and the operating system schedules the kernel to write the contents of the kernel buffer to disk.
-
This copying can be eliminated by directly applying for off-heap memory in the user process to store data that needs to be written to disk.
implementation
Java provides several ways to use off-heap memory and [DMA] to greatly optimize the IO efficiency of user processes. Here, I present a copy of the code, using BIO, NIO, and NIO using off-heap memory respectively to copy the file, and briefly compare their time.
Use a PDF file of about 200MB for copying. You can specify a larger file, the bigger the contrast. The latency that I have calculated here is around 1500ms on average for BIO, around 120ms for NIO, and around 100ms for NIO using off-heap memory.
package top.jiangnanmax.nio;
import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class CopyCompare {
public static void main(String[] args) throws Exception {
String inputFile = "/tmp/nio/input/HyperLedger.pdf";
String outputFile = "/tmp/nio/output/HyperLedger.pdf";
long start = System.currentTimeMillis();
nioCopyByDirectMem(inputFile, outputFile);
long end = System.currentTimeMillis();
System.out.println("cost time: " + (end - start) + " ms");
deleteFile(outputFile);
}
/** * File replication using traditional IO ** Takes 15** ms ** on average@param sourcePath
* @param destPath
*/
private static void bioCopy(String sourcePath, String destPath) throws Exception {
File sourceFile = new File(sourcePath);
File destFile = new File(destPath);
if(! destFile.exists()) { destFile.createNewFile(); } FileInputStream inputStream =new FileInputStream(sourceFile);
FileOutputStream outputStream = new FileOutputStream(destFile);
byte[] buffer = new byte[512];
int lenRead;
while((lenRead = inputStream.read(buffer)) ! = -1) {
outputStream.write(buffer, 0, lenRead);
}
inputStream.close();
outputStream.close();
}
/** * File copy using NIO, but without off-heap memory * takes an average of 1** ms, which is an order of magnitude faster than BIO directly. *@param sourcePath
* @param destPath
*/
private static void nioCopy(String sourcePath, String destPath) throws Exception {
File sourceFile = new File(sourcePath);
File destFile = new File(destPath);
if(! destFile.exists()) { destFile.createNewFile(); } FileInputStream inputStream =new FileInputStream(sourceFile);
FileOutputStream outputStream = new FileOutputStream(destFile);
FileChannel inputChannel = inputStream.getChannel();
FileChannel outputChannel = outputStream.getChannel();
// transferFrom should be called sendFile
// Data is transferred directly between the two file descriptors
// DMA
outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
inputChannel.close();
outputChannel.close();
inputStream.close();
outputStream.close();
}
/** * File replication using NIO and using out-of-heap memory * takes about 100ms on average, a bit faster than NIO without out-of-heap memory *@param sourcePath
* @param destPath
*/
private static void nioCopyByDirectMem(String sourcePath, String destPath) throws Exception {
File sourceFile = new File(sourcePath);
File destFile = new File(destPath);
if(! destFile.exists()) { destFile.createNewFile(); } FileInputStream inputStream =new FileInputStream(sourceFile);
FileOutputStream outputStream = new FileOutputStream(destFile);
FileChannel inputChannel = inputStream.getChannel();
FileChannel outputChannel = outputStream.getChannel();
MappedByteBuffer buffer = inputChannel.map(FileChannel.MapMode.READ_ONLY, 0, inputChannel.size());
outputChannel.write(buffer);
inputChannel.close();
outputChannel.close();
inputStream.close();
outputStream.close();
}
/** * delete target file **@param target
*/
private static void deleteFile(String target) {
File file = newFile(target); file.delete(); }}Copy the code