• How and Why Unsafe is Used in Java---reference


    By 

    https://www.voxxed.com/blog/2014/12/how-and-why-unsafe-is-used-in-java/

    Overview

    sun.misc.Unsafe has been in Java from at least as far back as Java 1.4 (2004).  In Java 9, Unsafe will be hidden along with many other, for-internal-use classes. to improve the maintainability of the JVM.  While it is still unclear exactly what will replace Unsafe, and I suspect it will be more than one thing which replaces it, it raises the question, why is it used at all?

    Doing things which the Java language doesn’t allow but are still useful.

    Java doesn’t allow many of the tricks which are available to lower level languages.  For most developers this is very good thing, and it not only saves you from yourself, it also saves you from your co-workers.  It also makes it easier to import open source code because you know there is limits to how much damage they can do.  Or at least there is limits to how much you can do accidentally. If you try hard enough you can still do damage.

    But why would you even try, you might wonder?  When building libraries many (but not all) of the methods in Unsafe are useful and in some cases, there is no other way to do the same thing without using JNI, which is even more dangerous and you lose the “compile once, run anywhere”

    Deserialization of objects

    When deserializing or building an object using a framework, you make the assumption you want to reconstitute an object which existed before.  You expect that you will use reflection to either call the setters of the class, or more likely set the internal fields directly, even the final fields.  The problem is you want to create an instance of an object, but you don’t really need a constructor as this is likely to only make things more difficult and have side effects.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class A implements Serializable {
        private final int num;
        public A(int num) {
            System.out.println("Hello Mum");
            this.num = num;
        }
     
        public int getNum() {
            return num;
        }
    }

    In this class, you should be able to rebuild and set the final field, but if you have to call a constructor and it might do things which don’t have anything to do with deserialization.  For these reasons many libraries use Unsafe to create instances without calling a constructor

    1
    2
    3
    Unsafe unsafe = getUnsafe();
    Class aClass = A.class;
    A a = (A) unsafe.allocateInstance(aClass);

    Calling allocateInstance avoids the need to call the appropriate constructor, when we don’t need one.

    Thread safe access to direct memory

    Another use for Unsafe is thread safe access to off heap memory.  ByteBuffer gives you safe access to off heap or direct memory, however it doesn’t have any thread safe operations.  This is particularly useful if you want to share data between processes.

    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
    import sun.misc.Unsafe;
    import sun.nio.ch.DirectBuffer;
     
    import java.io.File;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.lang.reflect.Field;
    import java.nio.MappedByteBuffer;
    import java.nio.channels.FileChannel;
     
    public class PingPongMapMain {
        public static void main(String... args) throws IOException {
            boolean odd;
            switch (args.length < 1 ? "usage" : args[0].toLowerCase()) {
                case "odd":
                    odd = true;
                    break;
                case "even":
                    odd = false;
                    break;
                default:
                    System.err.println("Usage: java PingPongMain [odd|even]");
                    return;        }
            int runs = 10000000;
            long start = 0;
            System.out.println("Waiting for the other odd/even");
            File counters = new File(System.getProperty("java.io.tmpdir"), "counters.deleteme");        counters.deleteOnExit();
     
            try (FileChannel fc = new RandomAccessFile(counters, "rw").getChannel()) {
                MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
                long address = ((DirectBuffer) mbb).address();
                for (int i = -1; i < runs; i++) {
                    for (; ; ) {
                        long value = UNSAFE.getLongVolatile(null, address);
                        boolean isOdd = (value & 1) != 0;
                        if (isOdd != odd)
                            // wait for the other side.
                            continue;
                        // make the change atomic, just in case there is more than one odd/even process
                        if (UNSAFE.compareAndSwapLong(null, address, value, value + 1))
                            break;
                    }
                    if (i == 0) {
                        System.out.println("Started");
                        start = System.nanoTime();
                    }
                }
            }
            System.out.printf("... Finished, average ping/pong took %,d ns%n",
                    (System.nanoTime() - start) / runs);
        }
     
        static final Unsafe UNSAFE;
     
        static {
            try {
                Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
                theUnsafe.setAccessible(true);
                UNSAFE = (Unsafe) theUnsafe.get(null);
            } catch (Exception e) {
                throw new AssertionError(e);
            }
        }
    }

    When you run this in two programs, one with odd and the other with even. You can see that each process is changing data via  persisted shared memory.

    In each program it maps the same are of the disks cache into the process.  There is actually only one copy of the file in memory.  This means the memory can be shared, provided you use thread safe operations such as the volatile and CAS operations.

    The output on an i7-3970X is

    Waiting for the other odd/even
    Started
    … Finished, average ping/pong took 83 ns

    That is 83 ns round trip time between two processes. When you consider System V IPC takes around 2,500 ns and IPC volatile instead of persisted, that is pretty quick.

    Is using Unsafe suitable for work?

    I wouldn’t recommend you use Unsafe directly.  It requires far more testing than natural Java development.  For this reason I suggest you use a library where it’s usage has been tested already.  If you wan to use Unsafe yourself, I suggest you thoughly test it’s usage in a stand alone library.  This limits how Unsafe is used in your application and give syou a safer, Unsafe.

    Conclusion

    It is interesting that Unsafe exists in Java, and you might to play with it at home.  It has some work applications especially in writing low level libraries, but in general it is better to use a library which uses Unsafe which has been tested than use it directly yourself.
  • 相关阅读:
    centos 7安装libreoffice
    python3-xlwt-Excel设置(字体大小、颜色、对齐方式、换行、合并单元格、边框、背景、下划线、斜体、加粗)
    PHP导出身份证号科学计数法
    PHP接收json格式的POST数据
    微信小程序知识
    搭建Vue开发环境的步骤
    公众号认证?小程序认证?小程序复用公众号资质进行认证?
    七牛云——批量将本地图片上传到七牛云
    身份认证接口
    php二维数组去重
  • 原文地址:https://www.cnblogs.com/davidwang456/p/4180715.html
Copyright © 2020-2023  润新知