当前位置:网站首页>[100 cases of JVM tuning practice] 05 - Method area tuning practice (Part 2)

[100 cases of JVM tuning practice] 05 - Method area tuning practice (Part 2)

2022-07-07 01:06:00 Half old 518

front said
Author's brief introduction : Half old 518, Long distance runner , Determined to persist in writing 10 Blog of the year , Focus on java Back end
Column Introduction : Case driven introduction JVM knowledge , Teach you how to use JVM Troubleshooting 、 Evaluation code 、 Optimize performance
The article brief introduction : Introduce the concept of method area 、 Help you deeply sort out direct memory

7.8 Direct memory

Direct memory is managed by the operating system . Common in NIO, For data buffering , High read and write performance , Allocation and recycling costs are high .

Use the following code to compare reading and writing using traditional methods with NIO The difference between reading and writing , Note that the first time you start reading and writing, the performance will be poor , Need to run several more times , Calculate average .

/**
 *  demonstration  ByteBuffer  effect 
 */
public class Demo1_9 {
    static final String FROM = "F:\\ Blog \\ Grain college practice program .md";
    static final String TO = "F:\\ Grain college practice program .md";
    static final int _1Mb = 1024 * 1024;

    public static void main(String[] args) {
        io(); // io  when :1535.586957 1766.963399 1359.240226
        directBuffer(); // directBuffer  when :479.295165 702.291454 562.56592
    }

    private static void directBuffer() {
        long start = System.nanoTime();
        try (FileChannel from = new FileInputStream(FROM).getChannel();
             FileChannel to = new FileOutputStream(TO).getChannel();
        ) {
            ByteBuffer bb = ByteBuffer.allocateDirect(_1Mb);
            while (true) {
                int len = from.read(bb);
                if (len == -1) {
                    break;
                }
                bb.flip();
                to.write(bb);
                bb.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        System.out.println("directBuffer  when :" + (end - start) / 1000_000.0);
    }

    private static void io() {
        long start = System.nanoTime();
        try (FileInputStream from = new FileInputStream(FROM);
             FileOutputStream to = new FileOutputStream(TO);
        ) {
            byte[] buf = new byte[_1Mb];
            while (true) {
                int len = from.read(buf);
                if (len == -1) {
                    break;
                }
                to.write(buf, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        System.out.println("io  when :" + (end - start) / 1000_000.0);
    }
}

Why is the efficiency of direct memory reading and writing high ? Use blocking io To read and write cpu The changes of and memory are shown in the figure below . Obviously , Copy files from the system cache to java A cache is a time-consuming and unnecessary replication .

 Insert picture description here

Use Nio To read and write cpu The changes of and memory are shown in the figure below . Operating system in allocateDirect() Method will allocate a piece of direct memory , This part of memory java Both the code and the system can be accessed .

 Insert picture description here

7.9 Memory overflow of direct memory

Direct memory direct memory It's not up to jvm Garbage collection , May cause memory leak problems . Run the following code .

/**
 *  Demonstrate direct memory overflow 
 */
public class Demo1_10 {
    static int _100Mb = 1024 * 1024 * 100;

    public static void main(String[] args) {
        List<ByteBuffer> list = new ArrayList<>();
        int i = 0;
        try {
            while (true) {
                ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_100Mb);
                list.add(byteBuffer);
                i++;
            }
        } finally {
            System.out.println(i);
        }
        //  The method area is jvm standard , jdk6  The implementation of the method area in is called permanent generation 
        //                  jdk8  The implementation of the method area is called meta space 
    }
}

Output results .

72
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
	at java.nio.Bits.reserveMemory(Bits.java:695)
	at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
	at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
	at cn.itcast.jvm.t1.direct.Demo1_10.main(Demo1_10.java:19)

What is the underlying recycling mechanism of direct memory ? Run the following code .

/**
 *  The impact of disabling explicit recycling on direct memory 
 */
public class Demo1_26 {
    static int _1Gb = 1024 * 1024 * 1024;

    /*
     * -XX:+DisableExplicitGC  Explicit 
     */
    public static void main(String[] args) throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1Gb);
        System.out.println(" Distribution finished ...");
        System.in.read();
        System.out.println(" Start releasing ...");
        byteBuffer = null;
        System.gc(); //  Explicit garbage collection ,Full GC
        System.in.read();
    }
}

After the console output is allocated , The memory usage can be seen from the task manager in the background .

 Insert picture description here

When entering enter on the console , The output begins to release , Enter enter again , This occupancy 1 individual G The process of memory is cleaned up . Does it mean java Of gc The operation works ?

Let's analyze the above process of direct memory recovery .Unsafe yes jdk A class at the bottom , For memory allocation , Memory recovery, etc , Ordinary programmers don't need to use , Here we get... Through reflection Unsafe object , Demonstrate the underlying principle of direct memory allocation .

/**
 *  The underlying principle of direct memory allocation :Unsafe
 */
public class Demo1_27 {
    static int _1Gb = 1024 * 1024 * 1024;

    public static void main(String[] args) throws IOException {
        Unsafe unsafe = getUnsafe();
        //  Allocate memory 
        long base = unsafe.allocateMemory(_1Gb);
        unsafe.setMemory(base, _1Gb, (byte) 0);
        System.in.read();

        //  Free memory 
        unsafe.freeMemory(base);
        System.in.read();
    }

    public static Unsafe getUnsafe() {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            Unsafe unsafe = (Unsafe) f.get(null);
            return unsafe;
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

Run code , Observe in the task manager jdk Process memory usage found , Memory usage will be in allocateMemory() Add... After 1G, stay freeMemory() After recovery . therefore , Direct memory recycling is not actually caused by jvm Virtual machine complete , But through Unsafe Object call freeMemory() complete .

See below ByteBuffer Class to verify our point of view .

allocateDirect() Back to one DirectByteBuffer object .

public static ByteBuffer allocateDirect(int capacity) {
      return new DirectByteBuffer(capacity);
}

call Unsafe in allocateMemory() To apply for memory , newly build Cleaner Object to free memory .

    DirectByteBuffer(int cap) {                   // package-private

        super(-1, 0, cap, cap);
        boolean pa = VM.isDirectMemoryPageAligned();
        int ps = Bits.pageSize();
        long size = Math.max(1L, (long)cap + (pa ? ps : 0));
        Bits.reserveMemory(size, cap);

        long base = 0;
        try {
            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;



    }

cleaner Relating to Deallocator What is it? ? Click in and see that it has been realized Runnable, Is the callback task object , stay run Method is called Unsafe Of freeMemory.

  private static class Deallocator
        implements Runnable
    {

        private static Unsafe unsafe = Unsafe.getUnsafe();

        private long address;
        private long size;
        private int capacity;

        private Deallocator(long address, long size, int capacity) {
            assert (address != 0);
            this.address = address;
            this.size = size;
            this.capacity = capacity;
        }

        public void run() {
            if (address == 0) {
                // Paranoia
                return;
            }
            unsafe.freeMemory(address);
            address = 0;
            Bits.unreserveMemory(size, capacity);
        }

    }

So when was the garbage collection task performed ? see Cleaner Source code .

public class Cleaner
    extends PhantomReference<Object> {
    //...
    public void clean() {
        if (!remove(this))
            return;
        try {
            thunk.run();
        } catch (final Throwable x) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        if (System.err != null)
                            new Error("Cleaner terminated abnormally", x)
                                .printStackTrace();
                        System.exit(1);
                        return null;
                    }});
        }
    }
    //...
    }

original Cleaner yes java Virtual reference type in , When its bound object is garbage collected , Will trigger a virtual reference clean() Method , Execute callback method run().

Now look back DirectByteBuffer Class Cleaner establish , The process is clear .

 cleaner = Cleaner.create(this, new Deallocator(base, size, cap));

Summarize direct memory allocation 、 The process of release is : By calling Unsafe Of allocateMemory To allocate direct memory , By creating a virtual reference object Cleaner object , take DirectoryByteBuffer Bind with callback task , When Directory When it's recycled , Automatically Cleaner Of clean() Method , To call Unsafe Of freeMemory() Free memory .

7.10 The impact of disabling explicit garbage collection on direct memory

stay java Can be used in System.gc() To make explicit suggestions jvm Garbage collection , But this way of garbage collection is Full GC, It will be recycled in the new generation , Old age recycling will also be carried out . May affect program performance . To avoid misuse by programmers , have access to -XX +DisableExplctGC To disable the garbage collection of the display .

Run again with explicit garbage collection disabled Demo1_26.

/**
 *  The impact of disabling explicit recycling on direct memory 
 */
public class Demo1_26 {
    static int _1Gb = 1024 * 1024 * 1024;

    /*
     * -XX:+DisableExplicitGC  Explicit 
     */
    public static void main(String[] args) throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1Gb);
        System.out.println(" Distribution finished ...");
        System.in.read();
        System.out.println(" Start releasing ...");
        byteBuffer = null;
        System.gc(); //  Explicit garbage collection ,Full GC
        System.in.read();
    }
}

The direct memory of the above code is not recycled , This is because explicit garbage collection fails .bytebuffer Will not be recycled , As a result, the direct memory cannot be released , Only when the program is passive Full GC Garbage collection . If the program needs to use direct memory frequently , We can receive and use Unsafe Object to allocate 、 Reclaiming memory .

原网站

版权声明
本文为[Half old 518]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/188/202207061710314298.html