Tag Archives: sun.misc.Unsafe

Forbidden Java actions: not declaring a checked exception; avoiding a constructor while creating an object

by Mikhail Vorontsov

This is the last article in the series of “Forbidden Java actions” articles. The two previous articles are Forbidden Java actions: object assignments, type conversions etc on the low level in Java and Forbidden Java actions: updating final and static final fields.

In this article we will see how to throw a checked exception in Java without declaring it in the method throws clause and how to create an object without calling any of its constructors.

Throwing a checked exception without letting know about it in the method signature

In Java you have to declare all checked exceptions in the throws clause of your method signature. This is a Java language requirement, JVM does not need this information. Let us prove it by finding a few ways to throw a checked exception while avoiding to declare it.

Thread.stop( Throwable )

The first way to throw a checked exception is to use a deprecated Thread.stop( Throwable ) method, which throws a given exception in the given thread. Don’t use this method in the production code! The following method will print “test” followed by “99”, which proves that thread won’t actually die 🙂

1
2
3
4
5
6
7
8
9
10
11
12
private static void threadStop()
{
    try
    {
        Thread.currentThread().stop( new IOException( "test" ) );
    }
    catch ( Exception ex )
    {
        System.out.println( ex.getMessage() );
    }
    System.out.println( 99 );
}
private static void threadStop()
{
    try
    {
        Thread.currentThread().stop( new IOException( "test" ) );
    }
    catch ( Exception ex )
    {
        System.out.println( ex.getMessage() );
    }
    System.out.println( 99 );
}

Continue reading

Forbidden Java actions: updating final and static final fields

by Mikhail Vorontsov

This article will discuss how you can update final or static final fields in Java. This is not allowed on the Java language level, but surprisingly, standard JDK classes will make it possible for you. Of course, we won’t forget a best Java hacker friend – sun.misc.Unsafe class from Oracle JDK. This is a second article in the “Forbidden Java actions” series started with Forbidden Java actions: object assignments, type conversions etc on the low level in Java.

Updating final or private fields

This is a simple trick. All you have to know about is a java.lang.reflect.AccessibleObject.setAccessible(boolean) method. If a field or method can not be accessed by Java language means in the current context (for example, you want to access a private field or method) – use this method with argument = true to obtain reflection access to that field or method. Unfortunately, this method is a subject of security checks, so it may be forbidden by a security manager in many enterprise server environments.

The following program will update a private final field of another class. It will print 20, despite the fact that a final field was initialized with 10 in the constructor.

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
public class FinalPrivateField {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        final A instance = new A( 10 );
        B.updateA( instance, 20 );
        System.out.println( instance.getField() );
    }
 
    public static class A
    {
        private final int m_field;
 
        public A( final int field ) {
            m_field = field;
        }
 
        public int getField() {
            return m_field;
        }
    }
 
    public static class B
    {
        public static void updateA( final A other, final int newValue ) throws NoSuchFieldException, IllegalAccessException
        {
            final Field fieldA = A.class.getDeclaredField( "m_field" );
            fieldA.setAccessible( true );
            fieldA.setInt( other, newValue );
        }
    }
}
public class FinalPrivateField {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        final A instance = new A( 10 );
        B.updateA( instance, 20 );
        System.out.println( instance.getField() );
    }

    public static class A
    {
        private final int m_field;

        public A( final int field ) {
            m_field = field;
        }

        public int getField() {
            return m_field;
        }
    }

    public static class B
    {
        public static void updateA( final A other, final int newValue ) throws NoSuchFieldException, IllegalAccessException
        {
            final Field fieldA = A.class.getDeclaredField( "m_field" );
            fieldA.setAccessible( true );
            fieldA.setInt( other, newValue );
        }
    }
}

Continue reading

Forbidden Java actions: object assignments, type conversions etc on the low level in Java

by Mikhail Vorontsov

This article will reveal you a few details about the low level Java memory layout: we will see how to implement Object assignments using just primitive types. Then we will see what’s hidden in the array header and will convert an array of one type into an array of another type. This article continues the memory allocation discussion from Various types of memory allocation in Java article. It is also the first article in the “Forbidden Java actions” series.

WARNING: It is not recommended to apply these ideas to a production code! This article has only exploratory tasks.

Object assignments on the low level

As we already know from the Memory introspection using sun.misc.Unsafe and reflection article, Object reference (not its contents!) occupies just 4 bytes on the under 32Gb heaps. We will assume 4 byte Object references in this article.

All examples from this article require the following static definition:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static final Unsafe unsafe;
static
{
    try
    {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        unsafe = (Unsafe)field.get(null);
    }
    catch (Exception e)
    {
        throw new RuntimeException(e);
    }
}
 
private static final long longArrayOffset = unsafe.arrayBaseOffset(long[].class);
private static final long intArrayOffset = unsafe.arrayBaseOffset(int[].class);
private static final long integerArrayOffset = unsafe.arrayBaseOffset(Integer[].class);
private static final Unsafe unsafe;
static
{
    try
    {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        unsafe = (Unsafe)field.get(null);
    }
    catch (Exception e)
    {
        throw new RuntimeException(e);
    }
}

private static final long longArrayOffset = unsafe.arrayBaseOffset(long[].class);
private static final long intArrayOffset = unsafe.arrayBaseOffset(int[].class);
private static final long integerArrayOffset = unsafe.arrayBaseOffset(Integer[].class);

Let’s allocate a small Integer[ 4 ] array and populate it with 1, 2, 3 and 4. After that let’s see what’s stored in the data cells of the array. We will read them as int values:

1
2
3
4
5
6
7
8
9
10
11
12
13
final Integer[] ar = new Integer[ 4 ];
ar[ 0 ] = 1;
ar[ 1 ] = 2;
ar[ 2 ] = 3;
ar[ 3 ] = 4;
 
//objects occupy 4 bytes for under 32G heaps
int[] ptrs = new int[ 4 ];
for ( int i = 0; i < 4; i++)
{
    ptrs[ i ] = unsafe.getInt(ar, integerArrayOffset + i * 4);
    System.out.println("Integer[" + i + "] = " + Integer.toHexString( ptrs[ i ] ) );
}
final Integer[] ar = new Integer[ 4 ];
ar[ 0 ] = 1;
ar[ 1 ] = 2;
ar[ 2 ] = 3;
ar[ 3 ] = 4;

//objects occupy 4 bytes for under 32G heaps
int[] ptrs = new int[ 4 ];
for ( int i = 0; i < 4; i++)
{
    ptrs[ i ] = unsafe.getInt(ar, integerArrayOffset + i * 4);
    System.out.println("Integer[" + i + "] = " + Integer.toHexString( ptrs[ i ] ) );
}

Here is an output from my computer. It will be different each time, but most likely the difference between cells will be equal to 16, which is an actual Integer memory footprint.

Integer[0] = f004e880
Integer[1] = f004e870
Integer[2] = f004e860
Integer[3] = f004e850

Unfortunately, these values are not pure pointers (in terms of C) – trying to use them with unsafe.getInt( long address ) will cause an access violation.

On the other hand, we can still work in terms of these values. For example, we can implement an assignment ar[ 0 ] = ar[ 1 ]:

Continue reading

Various types of memory allocation in Java

by Mikhail Vorontsov

This article discusses various types of a memory buffer allocation in Java. We will see how to treat any sort of Java buffer uniformly using sun.misc.Unsafe memory access methods. This article may be especially interesting for ex-C programmers willing to work with the memory on the lowest possible level in Java.

If you are more interested in general Java memory optimization, take a look at An overview of memory saving techniques in Java article in this blog as well as its following parts: one, two.

Array allocation limitations

Array size in Java is limited by the fact of using int as an array index. This means that you can not allocate an array with more than Integer.MAX_VALUE ( = 2^31 - 1 ) elements. This doesn’t mean that the longest chunk of memory you can allocate in Java is 2 Gb. You can allocate an array of bigger type instead. For example,

1
final long[] ar = new long[ Integer.MAX_VALUE ];
final long[] ar = new long[ Integer.MAX_VALUE ];

will allocate 16Gb - 8 bytes, if you have sufficiently high -Xmx Java setting (usually you should have about 50% more memory in heap – so in order to allocate 16Gb buffer, you will have to specify -Xmx24G (this is a general rule, actual required heap size may vary).

Unfortunately, you will be limited by your array element type in pure Java. The only useful class for working with arrays is a ByteBuffer, which offers methods for getting/writing various Java data types in the buffer (see Various methods of binary serialization in Java for more details). The disadvantage of a ByteBuffer – you are limited with byte[] as a source array type, which means a limitation of 2Gb for your buffer.

Treating any arrays as a byte buffer

For a while let’s assume that 2Gb buffers were not sufficient for our needs, but a 16Gb buffer will make us happy. We have allocated a long[], but want to treat this buffer as a byte array. We need to use a best C programmer friend in Java – sun.misc.Unsafe. This class has 2 sets of methods: getN( Object, offset ), where N is a result type for reading a value of given type from the given offset in the object and putN( Object, offset, value ) for writing a value at a given offset.

Unfortunately, these methods set or get only an individual value. If you want to copy data to/from an array, you will need one more Unsafe method: copyMemory(srcObject, srcOffset, destObject, destOffset, count). It works similar to System.arraycopy, but copies bytes instead of array elements.

In order to access array data using sun.misc.Unsafe, you will need 2 components:

Continue reading

Use case: Optimizing memory footprint of a read only csv file (Trove, Unsafe, ByteBuffer, data compression)

by Mikhail Vorontsov

Suppose your application has to obtain some data from an auxiliary data source, which could be a csv file. Your csv file will contain several fields, one of those is used as a field ID. You need to keep all that file in memory and provide fast access to records by an ID field. Your additional target is to consume as little memory as possible, while keeping access cost as low as possible. In this article we will process fake person records. Here is an example:

{id=idnum10, surname=Smith10, address=10 One Way Road, Springfield, NJ, DOB=01/11/1965, names=John Paul 10 Ringo}
    

All these records are generated by the following class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private static class DataGenerator
{
    private int cnt = 1;
 
    public Map<String, String> getNextEntry()
    {
        final Map<String, String> res = new HashMap<String, String>( 8 );
        res.put( ID, "idnum" + cnt );
        res.put( "names", "John Paul " + cnt + " Ringo" );
        res.put( "surname", "Smith" + cnt );
        res.put( "DOB", "01/" + two((cnt % 31) + 1) + "/1965" ); //date of birth, MM/DD/YYYY
        res.put( "address", cnt + " One Way Road, Springfield, NJ" );
        ++cnt;
        return res;
    }
 
    private String two( final int val )
    {
        return val < 10 ? "0" + val : Integer.toString( val );
    }
}
private static class DataGenerator
{
    private int cnt = 1;

    public Map<String, String> getNextEntry()
    {
        final Map<String, String> res = new HashMap<String, String>( 8 );
        res.put( ID, "idnum" + cnt );
        res.put( "names", "John Paul " + cnt + " Ringo" );
        res.put( "surname", "Smith" + cnt );
        res.put( "DOB", "01/" + two((cnt % 31) + 1) + "/1965" ); //date of birth, MM/DD/YYYY
        res.put( "address", cnt + " One Way Road, Springfield, NJ" );
        ++cnt;
        return res;
    }

    private String two( final int val )
    {
        return val < 10 ? "0" + val : Integer.toString( val );
    }
}

Simple approach – map of maps

We always have to start from something simple and easy to support. In this case it may be a map of maps: outer map is indexed by ID field and th inner ones are indexed by field names.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    /**
     * Initial version. Outer map indexed by a key field value, inner map is field name to field value.
     * All field names are interned, but we still have to pay for storing references to field names.
     */
    private static class SimpleMapStorage extends SimpleStorage
    {
        private final Map<String, Map<String, String>> m_data = new HashMap<String, Map<String, String>>( 1000 );
 
        public void addEntry( final Map<String, String> entry )
        {
            m_data.put( entry.get( ID ), entry );
        }
 
        public Map<String, String> getById( final String id )
        {
            return m_data.get( id );
        }
    }
    
    /**
     * Initial version. Outer map indexed by a key field value, inner map is field name to field value.
     * All field names are interned, but we still have to pay for storing references to field names.
     */
    private static class SimpleMapStorage extends SimpleStorage
    {
        private final Map<String, Map<String, String>> m_data = new HashMap<String, Map<String, String>>( 1000 );

        public void addEntry( final Map<String, String> entry )
        {
            m_data.put( entry.get( ID ), entry );
        }

        public Map<String, String> getById( final String id )
        {
            return m_data.get( id );
        }
    }
    

For testing purposes, all storage implementations will either implement Storage interface or extend SimpleStorage class. You will see the purpose of pack method in the more advanced examples.

1
2
3
4
5
6
7
8
9
10
11
12
13
private interface Storage
{
    public void addEntry( final Map<String, String> entry );
    public Map<String, String> getById( final String id );
    public void pack();
}
 
public static abstract class SimpleStorage implements Storage
{
    public void pack()
    {
    }
}
private interface Storage
{
    public void addEntry( final Map<String, String> entry );
    public Map<String, String> getById( final String id );
    public void pack();
}

public static abstract class SimpleStorage implements Storage
{
    public void pack()
    {
    }
}

All storage implementations will be tested by the following method:

1
2
3
4
5
6
7
8
9
10
11
private static void testStorage(final int recordCount, final Storage storage)
{
    final DataGenerator generator = new DataGenerator();
    for ( int i = 0; i < recordCount; ++i )
        storage.addEntry( generator.getNextEntry() );
    storage.pack();
    System.gc();
    final long mem = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
    System.out.println( storage.getClass().getName() + ": " + mem / 1024.0 / 1024.0 + " MB");
    System.out.println( storage.getById( "idnum10" ) ); //in order to retain storage in memory
}
private static void testStorage(final int recordCount, final Storage storage)
{
    final DataGenerator generator = new DataGenerator();
    for ( int i = 0; i < recordCount; ++i )
        storage.addEntry( generator.getNextEntry() );
    storage.pack();
    System.gc();
    final long mem = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
    System.out.println( storage.getClass().getName() + ": " + mem / 1024.0 / 1024.0 + " MB");
    System.out.println( storage.getById( "idnum10" ) ); //in order to retain storage in memory
}

For every implementation in this article we will try to create 1 million entries. SimpleMapStorage consumes 706 Mb to store 1M records. The actual data size is about 82 Mb, which means that this simple implementation wastes about 85% of consumed memory. Yes, straightforward solutions for big data storage do not work well in Java…

Continue reading

Memory introspection using sun.misc.Unsafe and reflection

by Mikhail Vorontsov

It is useful for a serious Java developer to realize how much memory is occupied by one or another Java object. You may have heard that we live in a world there memory is not an issue anymore. This may be true for your text editor ( though, try to open a large document with tons of embedded images and charts and see how much memory will be consumed by your favourite editor ). This may be true for a dedicated server software (at least, until your business would grow to a bigger market or you will run another piece of software on the same server). This is may even be true for a cloud-based software, if you are rich enough in order to pay a premium for a top-class server hardware.

Still, in the real world your software will once reach a point where it makes sense to spend money in its optimization rather than trying to obtain an even better hardware (currently the most you can get on a commodity class server is 64G RAM). At this point you will have to analyze your application and find which data structures consume most of application memory. The best tool for such task is a good profiler, but you can start with a cheaper approach of analyzing your objects inside your code. This article describes an Oracle JDK based ClassIntrospector class, which will allow you to analyze your application memory consumption.

I have already mentioned the Java object memory layout in the String packing part 1: converting characters to bytes article. For example, I have written that a 28 character long String should occupy 104 bytes before Java 1.7.0_06. To be honest, at the time of writing of that article I used my profiler to get a proof for my calculations. Now it is about time to implement a Java object introspector using pure Java and Oracle JDK specific sun.misc.Unsafe class.

We will use the following sun.misc.Unsafe methods:

Continue reading

Performance of various methods of binary serialization in Java

by Mikhail Vorontsov

We are going to find out what is the performance of binary serialization in Java. Following classes will be compared:

  • DataInputStream(ByteArrayInputStream) and its counterpart DataOutputStream(ByteArrayOutputStream)
  • See how synchronization affects ByteArrayInput/OutputStream and check performance of BAInputStream – copy of ByteArrayInputStream w/o synchronization
  • ByteBuffer in its 4 flavours – heap/direct, big/little endian
  • sun.misc.Unsafe – based memory operations on heap byte arrays

My experience has shown me that all these serialization methods depend on on data item size as well as on buffer/stream type. So, two sets of tests were written. First test works on an object having a single field – byte[500], while second test is using another object with another single field – long[500]. In case of ByteBuffer and Unsafe we will test both bulk operations and serialization of every array element as a separate method call.

Continue reading