├── .gitignore ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lombok.config ├── settings.gradle └── src ├── jmh └── java │ └── pl │ └── north93 │ └── arrays │ └── ObjectBenchmarks.java ├── main └── java │ └── pl │ └── north93 │ └── arrays │ ├── Array.java │ ├── ArrayType.java │ ├── BooleanArray.java │ ├── ByteArray.java │ ├── CharArray.java │ ├── DoubleArray.java │ ├── FloatArray.java │ ├── IntArray.java │ ├── LongArray.java │ ├── ShortArray.java │ ├── experiment │ ├── Example.java │ └── Experiment.java │ ├── impl │ ├── ArrayClassLoader.java │ ├── ArrayFactory.java │ ├── ArrayIterator.java │ ├── BaseArrayImpl.java │ ├── BooleanArrayImpl.java │ ├── ByteArrayImpl.java │ ├── CharArrayImpl.java │ ├── ClassCacheKey.java │ ├── ClassGenerator.java │ ├── DoubleArrayImpl.java │ ├── FloatArrayImpl.java │ ├── IntArrayImpl.java │ ├── LongArrayImpl.java │ ├── ObjectArrayImpl.java │ └── ShortArrayImpl.java │ └── jvm │ ├── HotSpotEstimator.java │ ├── HotSpotMemoryAccess.java │ └── MemoryAccess.java └── test └── java └── pl └── north93 └── arrays ├── BooleanArrayTest.java └── ObjectArrayTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | build 4 | out -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Arrayless Arrays 2 | ================ 3 | 4 | Have you ever dreamed about a programming language without arrays? 5 | It's your lucky day. 6 | 7 | Project inspired by [worse arrays](https://github.com/natanbc/worse-arrays). 8 | 9 | Example 10 | ------ 11 | 12 | ```java 13 | public class Example 14 | { 15 | public static void main(final String[] args) 16 | { 17 | final ArrayFactory factory = new ArrayFactory(); 18 | 19 | final Array stringArray = factory.getObjectArray(String.class, 10); 20 | stringArray.set(0, "it works"); 21 | 22 | final BooleanArray booleanArray = factory.getBooleanArray(10); 23 | booleanArray.setBoolean(0, true); 24 | } 25 | } 26 | ``` 27 | 28 | Benchmark 29 | --------- 30 | 31 | ``` 32 | Benchmark Mode Cnt Score Error Units 33 | Benchmarks.arraylessArrayFill1000 avgt 3 1787.231 ± 16.024 ns/op 34 | Benchmarks.arraylessArraySetOne avgt 3 2.631 ± 1.011 ns/op 35 | Benchmarks.javaArrayFill1000 avgt 3 754.024 ± 114.484 ns/op 36 | Benchmarks.javaArraySetOne avgt 3 1.651 ± 0.461 ns/op 37 | ``` 38 | 39 | Only one nanosecond lose on each set operation in comparison to native Java arrays. -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | maven { 5 | url 'https://plugins.gradle.org/m2/' 6 | } 7 | } 8 | dependencies { 9 | classpath 'me.champeau.gradle:jmh-gradle-plugin:0.5.0' 10 | } 11 | } 12 | 13 | plugins { 14 | id 'java' 15 | id 'io.freefair.lombok' version '4.1.6' 16 | id 'me.champeau.gradle.jmh' version '0.5.0' 17 | } 18 | 19 | apply plugin: 'me.champeau.gradle.jmh' 20 | apply plugin: 'io.freefair.lombok' 21 | 22 | group 'pl.north93.arrays' 23 | version '1.0-SNAPSHOT' 24 | 25 | sourceCompatibility = 11 26 | targetCompatibility = 11 27 | 28 | repositories { 29 | mavenCentral() 30 | } 31 | 32 | dependencies { 33 | // https://mvnrepository.com/artifact/org.ow2.asm/asm 34 | compile group: 'org.ow2.asm', name: 'asm', version: '7.3.1' 35 | 36 | // https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine 37 | testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.6.0' 38 | } 39 | 40 | test { 41 | useJUnitPlatform() 42 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/northpl93/ArraylessArrays/473bc4153bfe1123a6d8f22412c3c69c3765fafa/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStorePath=wrapper/dists 5 | zipStoreBase=GRADLE_USER_HOME -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS='"-Xmx64m"' 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /lombok.config: -------------------------------------------------------------------------------- 1 | # This file is generated by the 'io.freefair.lombok' Gradle plugin 2 | config.stopBubbling = true 3 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'ArraylessArrays' 2 | 3 | -------------------------------------------------------------------------------- /src/jmh/java/pl/north93/arrays/ObjectBenchmarks.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import org.openjdk.jmh.annotations.Benchmark; 6 | import org.openjdk.jmh.annotations.BenchmarkMode; 7 | import org.openjdk.jmh.annotations.Fork; 8 | import org.openjdk.jmh.annotations.Measurement; 9 | import org.openjdk.jmh.annotations.Mode; 10 | import org.openjdk.jmh.annotations.OutputTimeUnit; 11 | import org.openjdk.jmh.annotations.Scope; 12 | import org.openjdk.jmh.annotations.State; 13 | import org.openjdk.jmh.annotations.Warmup; 14 | import org.openjdk.jmh.infra.Blackhole; 15 | 16 | import lombok.ToString; 17 | import pl.north93.arrays.impl.ArrayFactory; 18 | 19 | @Fork(1) 20 | @BenchmarkMode(Mode.AverageTime) 21 | @Warmup(iterations = 5) 22 | @Measurement(iterations = 3) 23 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 24 | public class ObjectBenchmarks 25 | { 26 | // WRITE benchmarks 27 | 28 | //@Benchmark 29 | public void arraylessArrayFill1000(final ArraysHolder arraysHolder) 30 | { 31 | final Array arraylessArray = arraysHolder.arraylessArray; 32 | for (int i = 0; i < 1000; i++) 33 | { 34 | arraylessArray.set(i, arraysHolder); 35 | } 36 | } 37 | 38 | @Benchmark 39 | public void arraylessArraySetOne(final ArraysHolder arraysHolder) 40 | { 41 | final Array arraylessArray = arraysHolder.arraylessArray; 42 | arraylessArray.set(100, arraysHolder); 43 | } 44 | 45 | //@Benchmark 46 | public void javaArrayFill1000(final ArraysHolder arraysHolder) 47 | { 48 | final Object[] javaArray = arraysHolder.javaArray; 49 | for (int i = 0; i < 1000; i++) 50 | { 51 | javaArray[i] = arraysHolder; 52 | } 53 | } 54 | 55 | @Benchmark 56 | public void javaArraySetOne(final ArraysHolder arraysHolder) 57 | { 58 | final Object[] javaArray = arraysHolder.javaArray; 59 | javaArray[100] = arraysHolder; 60 | } 61 | 62 | // READ benchmarks 63 | 64 | //@Benchmark 65 | public void arraylessArrayRead1000(final Blackhole blackhole, final ArraysHolder arraysHolder) 66 | { 67 | final Array arraylessArray = arraysHolder.arraylessArray; 68 | for (int i = 0; i < 1000; i++) 69 | { 70 | blackhole.consume(arraylessArray.get(i)); 71 | } 72 | } 73 | 74 | @Benchmark 75 | public void arraylessArrayReadOne(final Blackhole blackhole, final ArraysHolder arraysHolder) 76 | { 77 | final Array arraylessArray = arraysHolder.arraylessArray; 78 | blackhole.consume(arraylessArray.get(100)); 79 | } 80 | 81 | //@Benchmark 82 | public void javaArrayRead1000(final Blackhole blackhole, final ArraysHolder arraysHolder) 83 | { 84 | final Object[] javaArray = arraysHolder.javaArray; 85 | for (int i = 0; i < 1000; i++) 86 | { 87 | blackhole.consume(javaArray[i]); 88 | } 89 | } 90 | 91 | @Benchmark 92 | public void javaArrayReadOne(final Blackhole blackhole, final ArraysHolder arraysHolder) 93 | { 94 | final Object[] javaArray = arraysHolder.javaArray; 95 | blackhole.consume(javaArray[100]); 96 | } 97 | 98 | //@Benchmark 99 | //@Fork(jvmArgsAppend = { "-XX:+UnlockExperimentalVMOptions", "-XX:+TrustFinalNonStaticFields" }) 100 | public void arraylessArrayWriteWithTrustFinalNonStaticFields(final ArraysHolder arraysHolder) 101 | { 102 | final Array arraylessArray = arraysHolder.arraylessArray; 103 | arraylessArray.set(100, arraysHolder); 104 | } 105 | 106 | @ToString 107 | @State(Scope.Benchmark) 108 | public static class ArraysHolder 109 | { 110 | public final Object[] javaArray; 111 | public final Array arraylessArray; 112 | 113 | public ArraysHolder() 114 | { 115 | this.javaArray = new Object[1000]; 116 | 117 | final ArrayFactory arrayFactory = new ArrayFactory(); 118 | this.arraylessArray = arrayFactory.getObjectArray(Object.class, 1000); 119 | } 120 | } 121 | } 122 | 123 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/Array.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays; 2 | 3 | public interface Array extends Iterable 4 | { 5 | int size(); 6 | 7 | T get(int index); 8 | 9 | void set(int index, T value); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/ArrayType.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.ToString; 6 | 7 | @Getter 8 | @ToString 9 | @AllArgsConstructor 10 | public enum ArrayType 11 | { 12 | OBJECT("Ljava/lang/Object;"), 13 | BOOLEAN("Z"), 14 | BYTE("B"), 15 | CHAR("C"), 16 | SHORT("S"), 17 | INTEGER("I"), 18 | FLOAT("F"), 19 | LONG("J"), 20 | DOUBLE("D"); 21 | 22 | private final String fieldDescriptor; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/BooleanArray.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays; 2 | 3 | public interface BooleanArray extends Array 4 | { 5 | boolean getBoolean(int index); 6 | 7 | void setBoolean(int index, boolean value); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/ByteArray.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays; 2 | 3 | public interface ByteArray extends Array 4 | { 5 | byte getByte(int index); 6 | 7 | void setByte(int index, byte value); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/CharArray.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays; 2 | 3 | public interface CharArray extends Array 4 | { 5 | char getChar(int index); 6 | 7 | void setChar(int index, char value); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/DoubleArray.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays; 2 | 3 | public interface DoubleArray extends Array 4 | { 5 | double getDouble(int index); 6 | 7 | void setDouble(int index, double value); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/FloatArray.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays; 2 | 3 | public interface FloatArray extends Array 4 | { 5 | float getFloat(int index); 6 | 7 | void setFloat(int index, float value); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/IntArray.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays; 2 | 3 | public interface IntArray extends Array 4 | { 5 | int getInt(int index); 6 | 7 | void setInt(int index, int value); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/LongArray.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays; 2 | 3 | public interface LongArray extends Array 4 | { 5 | long getLong(int index); 6 | 7 | void setLong(int index, long value); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/ShortArray.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays; 2 | 3 | public interface ShortArray extends Array 4 | { 5 | short getShort(int index); 6 | 7 | void setShort(int index, short value); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/experiment/Example.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.experiment; 2 | 3 | import pl.north93.arrays.Array; 4 | import pl.north93.arrays.BooleanArray; 5 | import pl.north93.arrays.impl.ArrayFactory; 6 | 7 | public class Example 8 | { 9 | public static void main(final String[] args) 10 | { 11 | final ArrayFactory factory = new ArrayFactory(); 12 | 13 | final Array stringArray = factory.getObjectArray(String.class, 10); 14 | stringArray.set(0, "it works"); 15 | 16 | final BooleanArray booleanArray = factory.getBooleanArray(10); 17 | booleanArray.setBoolean(0, true); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/experiment/Experiment.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.experiment; 2 | 3 | import pl.north93.arrays.Array; 4 | import pl.north93.arrays.impl.ArrayFactory; 5 | 6 | public class Experiment 7 | { 8 | //private static Array staticArray; 9 | 10 | public static void main(final String... args) throws Exception 11 | { 12 | scope(); 13 | 14 | for (int i = 0; i < 5; i++) 15 | { 16 | System.gc(); 17 | Thread.sleep(1000); 18 | } 19 | 20 | // staticArray = null; 21 | // 22 | // for (int i = 0; i < 5; i++) 23 | // { 24 | // System.gc(); 25 | // Thread.sleep(1000); 26 | // } 27 | 28 | Thread.sleep(5000); 29 | } 30 | 31 | private static void scope() 32 | { 33 | final ArrayFactory arrayFactory = new ArrayFactory(); 34 | 35 | final Array array = arrayFactory.getObjectArray(Object.class, 10); 36 | //staticArray = array; 37 | 38 | System.out.println(array); 39 | 40 | for (int i = 0; i < 9; i++) 41 | { 42 | array.set(i, new Object()); 43 | System.out.println(array.get(i)); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/impl/ArrayClassLoader.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.impl; 2 | 3 | final class ArrayClassLoader extends ClassLoader 4 | { 5 | public Class loadClass(final String name, final byte[] bytes) 6 | { 7 | final String withDots = name.replace('/', '.'); 8 | return this.defineClass(withDots, bytes, 0, bytes.length); 9 | } 10 | 11 | public static String getClassName(final String simpleName) 12 | { 13 | final String originalName = ArrayClassLoader.class.getPackageName(); 14 | 15 | final String nameWithSlashes = originalName.replace('.', '/'); 16 | return nameWithSlashes + "/" + simpleName; 17 | } 18 | 19 | static 20 | { 21 | registerAsParallelCapable(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/impl/ArrayFactory.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.impl; 2 | 3 | import pl.north93.arrays.Array; 4 | import pl.north93.arrays.ArrayType; 5 | import pl.north93.arrays.BooleanArray; 6 | import pl.north93.arrays.ByteArray; 7 | import pl.north93.arrays.CharArray; 8 | import pl.north93.arrays.DoubleArray; 9 | import pl.north93.arrays.FloatArray; 10 | import pl.north93.arrays.IntArray; 11 | import pl.north93.arrays.LongArray; 12 | import pl.north93.arrays.ShortArray; 13 | import pl.north93.arrays.jvm.HotSpotMemoryAccess; 14 | import pl.north93.arrays.jvm.MemoryAccess; 15 | 16 | public class ArrayFactory 17 | { 18 | private final MemoryAccess memoryAccess = new HotSpotMemoryAccess(); 19 | private final ClassGenerator classGenerator = new ClassGenerator(); 20 | 21 | public Array getObjectArray(Class type, final int size) 22 | { 23 | final Object storage = this.createStorageObject(ArrayType.OBJECT, size); 24 | return new ObjectArrayImpl<>(this.memoryAccess, storage, size); 25 | } 26 | 27 | public BooleanArray getBooleanArray(final int size) 28 | { 29 | final Object storage = this.createStorageObject(ArrayType.BOOLEAN, size); 30 | return new BooleanArrayImpl(this.memoryAccess, storage, size); 31 | } 32 | 33 | public ByteArray getByteArray(final int size) 34 | { 35 | final Object storage = this.createStorageObject(ArrayType.BYTE, size); 36 | return new ByteArrayImpl(this.memoryAccess, storage, size); 37 | } 38 | 39 | public CharArray getCharArray(final int size) 40 | { 41 | final Object storage = this.createStorageObject(ArrayType.CHAR, size); 42 | return new CharArrayImpl(this.memoryAccess, storage, size); 43 | } 44 | 45 | public ShortArray getShortArray(final int size) 46 | { 47 | final Object storage = this.createStorageObject(ArrayType.SHORT, size); 48 | return new ShortArrayImpl(this.memoryAccess, storage, size); 49 | } 50 | 51 | public IntArray getIntArray(final int size) 52 | { 53 | final Object storage = this.createStorageObject(ArrayType.INTEGER, size); 54 | return new IntArrayImpl(this.memoryAccess, storage, size); 55 | } 56 | 57 | public FloatArray getFloatArray(final int size) 58 | { 59 | final Object storage = this.createStorageObject(ArrayType.FLOAT, size); 60 | return new FloatArrayImpl(this.memoryAccess, storage, size); 61 | } 62 | 63 | public LongArray getLongArray(final int size) 64 | { 65 | final Object storage = this.createStorageObject(ArrayType.LONG, size); 66 | return new LongArrayImpl(this.memoryAccess, storage, size); 67 | } 68 | 69 | public DoubleArray getDoubleArray(final int size) 70 | { 71 | final Object storage = this.createStorageObject(ArrayType.DOUBLE, size); 72 | return new DoubleArrayImpl(this.memoryAccess, storage, size); 73 | } 74 | 75 | private Object createStorageObject(final ArrayType arrayType, final int size) 76 | { 77 | if (size < 0) 78 | { 79 | throw new IllegalArgumentException("Negative array size"); 80 | } 81 | else if (size == 0) 82 | { 83 | return null; 84 | } 85 | 86 | final Class clazz = this.classGenerator.getClassForArray(arrayType, size); 87 | return this.memoryAccess.instantiate(clazz); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/impl/ArrayIterator.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.impl; 2 | 3 | import java.util.Iterator; 4 | 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.ToString; 7 | 8 | @ToString 9 | @RequiredArgsConstructor 10 | final class ArrayIterator implements Iterator 11 | { 12 | private final BaseArrayImpl array; 13 | private int index; 14 | 15 | @Override 16 | public boolean hasNext() 17 | { 18 | return this.index < this.array.size(); 19 | } 20 | 21 | @Override 22 | public T next() 23 | { 24 | return this.array.get(this.index++); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/impl/BaseArrayImpl.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.impl; 2 | 3 | import java.util.Iterator; 4 | 5 | import lombok.AllArgsConstructor; 6 | import pl.north93.arrays.Array; 7 | import pl.north93.arrays.jvm.MemoryAccess; 8 | 9 | @AllArgsConstructor 10 | abstract class BaseArrayImpl implements Array 11 | { 12 | protected final MemoryAccess memoryAccess; 13 | protected final Object storage; 14 | private final int size; 15 | 16 | @Override 17 | public final int size() 18 | { 19 | return this.size; 20 | } 21 | 22 | protected final void checkIndex(final int index) 23 | { 24 | if (index < 0 || index > this.size) 25 | { 26 | throw new ArrayIndexOutOfBoundsException(); 27 | } 28 | } 29 | 30 | @Override 31 | public Iterator iterator() 32 | { 33 | return new ArrayIterator<>(this); 34 | } 35 | 36 | @Override 37 | public String toString() 38 | { 39 | final StringBuilder builder = new StringBuilder("["); 40 | 41 | final int lastElement = this.size - 1; 42 | for (int i = 0; i < this.size; i++) 43 | { 44 | builder.append(this.get(i)); 45 | if (i != lastElement) 46 | { 47 | builder.append(", "); 48 | } 49 | } 50 | 51 | builder.append("]"); 52 | return builder.toString(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/impl/BooleanArrayImpl.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.impl; 2 | 3 | import pl.north93.arrays.BooleanArray; 4 | import pl.north93.arrays.jvm.MemoryAccess; 5 | 6 | class BooleanArrayImpl extends BaseArrayImpl implements BooleanArray 7 | { 8 | public BooleanArrayImpl(final MemoryAccess memoryAccess, final Object storage, final int size) 9 | { 10 | super(memoryAccess, storage, size); 11 | } 12 | 13 | @Override 14 | public boolean getBoolean(final int index) 15 | { 16 | this.checkIndex(index); 17 | return this.memoryAccess.readBooleanFrom(this.storage, index); 18 | } 19 | 20 | @Override 21 | public void setBoolean(final int index, final boolean value) 22 | { 23 | this.checkIndex(index); 24 | this.memoryAccess.setBooleanIn(this.storage, index, value); 25 | } 26 | 27 | @Override 28 | public Boolean get(final int index) 29 | { 30 | return this.getBoolean(index); 31 | } 32 | 33 | @Override 34 | public void set(final int index, final Boolean value) 35 | { 36 | this.setBoolean(index, value); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/impl/ByteArrayImpl.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.impl; 2 | 3 | import pl.north93.arrays.ByteArray; 4 | import pl.north93.arrays.jvm.MemoryAccess; 5 | 6 | class ByteArrayImpl extends BaseArrayImpl implements ByteArray 7 | { 8 | public ByteArrayImpl(final MemoryAccess memoryAccess, final Object storage, final int size) 9 | { 10 | super(memoryAccess, storage, size); 11 | } 12 | 13 | @Override 14 | public byte getByte(final int index) 15 | { 16 | this.checkIndex(index); 17 | return this.memoryAccess.readByteFrom(this.storage, index); 18 | } 19 | 20 | @Override 21 | public void setByte(final int index, final byte value) 22 | { 23 | this.checkIndex(index); 24 | this.memoryAccess.setByteIn(this.storage, index, value); 25 | } 26 | 27 | @Override 28 | public Byte get(final int index) 29 | { 30 | return this.getByte(index); 31 | } 32 | 33 | @Override 34 | public void set(final int index, final Byte value) 35 | { 36 | this.setByte(index, value); 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/impl/CharArrayImpl.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.impl; 2 | 3 | import pl.north93.arrays.CharArray; 4 | import pl.north93.arrays.jvm.MemoryAccess; 5 | 6 | class CharArrayImpl extends BaseArrayImpl implements CharArray 7 | { 8 | public CharArrayImpl(final MemoryAccess memoryAccess, final Object storage, final int size) 9 | { 10 | super(memoryAccess, storage, size); 11 | } 12 | 13 | @Override 14 | public char getChar(final int index) 15 | { 16 | this.checkIndex(index); 17 | return this.memoryAccess.readCharFrom(this.storage, index); 18 | } 19 | 20 | @Override 21 | public void setChar(final int index, final char value) 22 | { 23 | this.checkIndex(index); 24 | this.memoryAccess.setCharIn(this.storage, index, value); 25 | } 26 | 27 | @Override 28 | public Character get(final int index) 29 | { 30 | return this.getChar(index); 31 | } 32 | 33 | @Override 34 | public void set(final int index, final Character value) 35 | { 36 | this.setChar(index, value); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/impl/ClassCacheKey.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.impl; 2 | 3 | import lombok.Data; 4 | import pl.north93.arrays.ArrayType; 5 | 6 | @Data // allArgsConstructor, getters, equals&hashCode 7 | class ClassCacheKey 8 | { 9 | private final int size; 10 | private final ArrayType type; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/impl/ClassGenerator.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.impl; 2 | 3 | import java.util.Map; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | 6 | import org.objectweb.asm.ClassWriter; 7 | import org.objectweb.asm.FieldVisitor; 8 | import org.objectweb.asm.MethodVisitor; 9 | import org.objectweb.asm.Opcodes; 10 | 11 | import lombok.ToString; 12 | import pl.north93.arrays.ArrayType; 13 | 14 | @ToString 15 | final class ClassGenerator implements Opcodes 16 | { 17 | private final Map> classCache = new ConcurrentHashMap<>(); 18 | private final ArrayClassLoader classLoader = new ArrayClassLoader(); 19 | 20 | public Class getClassForArray(final ArrayType arrayType, final int fields) 21 | { 22 | final ClassCacheKey cacheKey = new ClassCacheKey(fields, arrayType); 23 | return this.classCache.computeIfAbsent(cacheKey, this::generateClass); 24 | } 25 | 26 | private Class generateClass(final ClassCacheKey cacheKey) 27 | { 28 | final ClassWriter classWriter = new ClassWriter(0); 29 | 30 | final String className = this.generateClassName(cacheKey); 31 | classWriter.visit(V11, ACC_PUBLIC + ACC_FINAL, className, "Ljava/lang/Object;", "java/lang/Object", null); 32 | 33 | // we don't need constructor because we're using Unsafe#allocateInstance 34 | //this.generateConstructor(classWriter); 35 | 36 | for (int i = 0; i < cacheKey.getSize(); i++) 37 | { 38 | final String fieldName = Integer.toString(i); 39 | final String fieldDescriptor = cacheKey.getType().getFieldDescriptor(); 40 | 41 | final FieldVisitor fieldVisitor = classWriter.visitField(ACC_PUBLIC, fieldName, fieldDescriptor, null, null); 42 | fieldVisitor.visitEnd(); 43 | } 44 | 45 | classWriter.visitEnd(); 46 | 47 | final byte[] byteCode = classWriter.toByteArray(); 48 | return this.classLoader.loadClass(className, byteCode); 49 | } 50 | 51 | private String generateClassName(final ClassCacheKey cacheKey) 52 | { 53 | final String typeName = cacheKey.getType().name(); 54 | return ArrayClassLoader.getClassName("ARRAY_" + typeName + "_" + cacheKey.getSize()); 55 | } 56 | 57 | private void generateConstructor(final ClassWriter classWriter) 58 | { 59 | final MethodVisitor visitor = classWriter.visitMethod(ACC_PUBLIC, "", "()V", null, null); 60 | visitor.visitCode(); 61 | visitor.visitVarInsn(ALOAD, 0); 62 | visitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); 63 | visitor.visitInsn(RETURN); 64 | visitor.visitMaxs(1, 1); 65 | visitor.visitEnd(); 66 | } 67 | } -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/impl/DoubleArrayImpl.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.impl; 2 | 3 | import pl.north93.arrays.DoubleArray; 4 | import pl.north93.arrays.jvm.MemoryAccess; 5 | 6 | class DoubleArrayImpl extends BaseArrayImpl implements DoubleArray 7 | { 8 | public DoubleArrayImpl(final MemoryAccess memoryAccess, final Object storage, final int size) 9 | { 10 | super(memoryAccess, storage, size); 11 | } 12 | 13 | @Override 14 | public double getDouble(final int index) 15 | { 16 | this.checkIndex(index); 17 | return this.memoryAccess.readDoubleFrom(this.storage, index); 18 | } 19 | 20 | @Override 21 | public void setDouble(final int index, final double value) 22 | { 23 | this.checkIndex(index); 24 | this.memoryAccess.setDoubleIn(this.storage, index, value); 25 | } 26 | 27 | @Override 28 | public Double get(final int index) 29 | { 30 | return this.getDouble(index); 31 | } 32 | 33 | @Override 34 | public void set(final int index, final Double value) 35 | { 36 | this.setDouble(index, value); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/impl/FloatArrayImpl.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.impl; 2 | 3 | import pl.north93.arrays.FloatArray; 4 | import pl.north93.arrays.jvm.MemoryAccess; 5 | 6 | class FloatArrayImpl extends BaseArrayImpl implements FloatArray 7 | { 8 | public FloatArrayImpl(final MemoryAccess memoryAccess, final Object storage, final int size) 9 | { 10 | super(memoryAccess, storage, size); 11 | } 12 | 13 | @Override 14 | public float getFloat(final int index) 15 | { 16 | this.checkIndex(index); 17 | return this.memoryAccess.readFloatFrom(this.storage, index); 18 | } 19 | 20 | @Override 21 | public void setFloat(final int index, final float value) 22 | { 23 | this.checkIndex(index); 24 | this.memoryAccess.setFloatIn(this.storage, index, value); 25 | } 26 | 27 | @Override 28 | public Float get(final int index) 29 | { 30 | return this.getFloat(index); 31 | } 32 | 33 | @Override 34 | public void set(final int index, final Float value) 35 | { 36 | this.setFloat(index, value); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/impl/IntArrayImpl.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.impl; 2 | 3 | import pl.north93.arrays.IntArray; 4 | import pl.north93.arrays.jvm.MemoryAccess; 5 | 6 | class IntArrayImpl extends BaseArrayImpl implements IntArray 7 | { 8 | public IntArrayImpl(final MemoryAccess memoryAccess, final Object storage, final int size) 9 | { 10 | super(memoryAccess, storage, size); 11 | } 12 | 13 | @Override 14 | public int getInt(final int index) 15 | { 16 | this.checkIndex(index); 17 | return this.memoryAccess.readIntFrom(this.storage, index); 18 | } 19 | 20 | @Override 21 | public void setInt(final int index, final int value) 22 | { 23 | this.checkIndex(index); 24 | this.memoryAccess.setIntIn(this.storage, index, value); 25 | } 26 | 27 | @Override 28 | public Integer get(final int index) 29 | { 30 | return this.getInt(index); 31 | } 32 | 33 | @Override 34 | public void set(final int index, final Integer value) 35 | { 36 | this.setInt(index, value); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/impl/LongArrayImpl.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.impl; 2 | 3 | import pl.north93.arrays.LongArray; 4 | import pl.north93.arrays.jvm.MemoryAccess; 5 | 6 | class LongArrayImpl extends BaseArrayImpl implements LongArray 7 | { 8 | public LongArrayImpl(final MemoryAccess memoryAccess, final Object storage, final int size) 9 | { 10 | super(memoryAccess, storage, size); 11 | } 12 | 13 | @Override 14 | public long getLong(final int index) 15 | { 16 | this.checkIndex(index); 17 | return this.memoryAccess.readLongFrom(this.storage, index); 18 | } 19 | 20 | @Override 21 | public void setLong(final int index, final long value) 22 | { 23 | this.checkIndex(index); 24 | this.memoryAccess.setLongIn(this.storage, index, value); 25 | } 26 | 27 | @Override 28 | public Long get(final int index) 29 | { 30 | return this.getLong(index); 31 | } 32 | 33 | @Override 34 | public void set(final int index, final Long value) 35 | { 36 | this.setLong(index, value); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/impl/ObjectArrayImpl.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.impl; 2 | 3 | import pl.north93.arrays.jvm.MemoryAccess; 4 | 5 | class ObjectArrayImpl extends BaseArrayImpl 6 | { 7 | public ObjectArrayImpl(final MemoryAccess memoryAccess, final Object storage, final int size) 8 | { 9 | super(memoryAccess, storage, size); 10 | } 11 | 12 | @Override 13 | @SuppressWarnings("unchecked") 14 | public T get(final int index) 15 | { 16 | this.checkIndex(index); 17 | return (T) this.memoryAccess.readObjectFrom(this.storage, index); 18 | } 19 | 20 | @Override 21 | public void set(final int index, final T value) 22 | { 23 | this.checkIndex(index); 24 | this.memoryAccess.setObjectIn(this.storage, index, value); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/impl/ShortArrayImpl.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.impl; 2 | 3 | import pl.north93.arrays.ShortArray; 4 | import pl.north93.arrays.jvm.MemoryAccess; 5 | 6 | class ShortArrayImpl extends BaseArrayImpl implements ShortArray 7 | { 8 | public ShortArrayImpl(final MemoryAccess memoryAccess, final Object storage, final int size) 9 | { 10 | super(memoryAccess, storage, size); 11 | } 12 | 13 | @Override 14 | public short getShort(final int index) 15 | { 16 | this.checkIndex(index); 17 | return this.memoryAccess.readShortFrom(this.storage, index); 18 | } 19 | 20 | @Override 21 | public void setShort(final int index, final short value) 22 | { 23 | this.checkIndex(index); 24 | this.memoryAccess.setShortIn(this.storage, index, value); 25 | } 26 | 27 | @Override 28 | public Short get(final int index) 29 | { 30 | return this.getShort(index); 31 | } 32 | 33 | @Override 34 | public void set(final int index, final Short value) 35 | { 36 | this.setShort(index, value); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/jvm/HotSpotEstimator.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.jvm; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | import lombok.ToString; 6 | import lombok.experimental.UtilityClass; 7 | import sun.misc.Unsafe; 8 | 9 | @UtilityClass 10 | class HotSpotEstimator 11 | { 12 | public static long estimateHeaderSize(final Unsafe unsafe) 13 | { 14 | final Field firstField = FieldHolder.class.getDeclaredFields()[0]; 15 | return unsafe.objectFieldOffset(firstField); 16 | } 17 | 18 | public static long estimateOopSize(final Unsafe unsafe) 19 | { 20 | final Field[] fields = FieldHolder.class.getDeclaredFields(); 21 | 22 | final long firstOffset = unsafe.objectFieldOffset(fields[0]); 23 | final long secondOffset = unsafe.objectFieldOffset(fields[1]); 24 | 25 | return secondOffset - firstOffset; 26 | } 27 | 28 | @ToString(onlyExplicitlyIncluded = true) 29 | private static class FieldHolder 30 | { 31 | private final Object oop1 = null, oop2 = null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/jvm/HotSpotMemoryAccess.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.jvm; 2 | 3 | import java.lang.reflect.Field; 4 | import java.text.MessageFormat; 5 | 6 | import sun.misc.Unsafe; 7 | 8 | public final class HotSpotMemoryAccess implements MemoryAccess 9 | { 10 | private static final Unsafe UNSAFE; 11 | private static final long LONG_SIZE = 8; 12 | private static final long DOUBLE_SIZE = 8; 13 | private static final long INTEGER_SIZE = 4; 14 | private static final long FLOAT_SIZE = 4; 15 | private static final long SHORT_SIZE = 2; 16 | private static final long CHAR_SIZE = 2; 17 | private static final long BYTE_SIZE = 1; 18 | private static final long BOOLEAN_SIZE = 1; 19 | private static final long OOP_SIZE; 20 | private static final long HEADER_SIZE; 21 | 22 | @Override 23 | public Object instantiate(final Class clazz) 24 | { 25 | try 26 | { 27 | return UNSAFE.allocateInstance(clazz); 28 | } 29 | catch (final InstantiationException e) 30 | { 31 | throw new RuntimeException("Failed to instantiate class via Unsafe", e); 32 | } 33 | } 34 | 35 | @Override 36 | public Object readObjectFrom(final Object object, final int index) 37 | { 38 | //System.out.println("get(" + object + ", " + offset + ")"); 39 | final long offset = this.getFieldLocation(OOP_SIZE, index); 40 | return UNSAFE.getObject(object, offset); 41 | } 42 | 43 | @Override 44 | public void setObjectIn(final Object object, final int index, final Object value) 45 | { 46 | //System.out.println("set(" + object + ", " + offset + ", " + value + ")"); 47 | final long offset = this.getFieldLocation(OOP_SIZE, index); 48 | UNSAFE.putObject(object, offset, value); 49 | } 50 | 51 | @Override 52 | public boolean readBooleanFrom(final Object object, final int index) 53 | { 54 | final long offset = this.getFieldLocation(BOOLEAN_SIZE, index); 55 | return UNSAFE.getBoolean(object, offset); 56 | } 57 | 58 | @Override 59 | public void setBooleanIn(final Object object, final int index, final boolean value) 60 | { 61 | final long offset = this.getFieldLocation(BOOLEAN_SIZE, index); 62 | UNSAFE.putBoolean(object, offset, value); 63 | } 64 | 65 | @Override 66 | public byte readByteFrom(final Object object, final int index) 67 | { 68 | final long offset = this.getFieldLocation(BYTE_SIZE, index); 69 | return UNSAFE.getByte(object, offset); 70 | } 71 | 72 | @Override 73 | public void setByteIn(final Object object, final int index, final byte value) 74 | { 75 | final long offset = this.getFieldLocation(BYTE_SIZE, index); 76 | UNSAFE.putByte(object, offset, value); 77 | } 78 | 79 | @Override 80 | public short readShortFrom(final Object object, final int index) 81 | { 82 | final long offset = this.getFieldLocation(SHORT_SIZE, index); 83 | return UNSAFE.getShort(object, offset); 84 | } 85 | 86 | @Override 87 | public void setShortIn(final Object object, final int index, final short value) 88 | { 89 | final long offset = this.getFieldLocation(SHORT_SIZE, index); 90 | UNSAFE.putShort(object, offset, value); 91 | } 92 | 93 | @Override 94 | public char readCharFrom(final Object object, final int index) 95 | { 96 | final long offset = this.getFieldLocation(CHAR_SIZE, index); 97 | return UNSAFE.getChar(object, offset); 98 | } 99 | 100 | @Override 101 | public void setCharIn(final Object object, final int index, final char value) 102 | { 103 | final long offset = this.getFieldLocation(CHAR_SIZE, index); 104 | UNSAFE.putChar(object, offset, value); 105 | } 106 | 107 | @Override 108 | public int readIntFrom(final Object object, final int index) 109 | { 110 | final long offset = this.getFieldLocation(INTEGER_SIZE, index); 111 | return UNSAFE.getInt(object, offset); 112 | } 113 | 114 | @Override 115 | public void setIntIn(final Object object, final int index, final int value) 116 | { 117 | final long offset = this.getFieldLocation(INTEGER_SIZE, index); 118 | UNSAFE.putInt(object, offset, value); 119 | } 120 | 121 | @Override 122 | public long readLongFrom(final Object object, final int index) 123 | { 124 | final long offset = this.getFieldLocation(LONG_SIZE, index); 125 | return UNSAFE.getLong(object, offset); 126 | } 127 | 128 | @Override 129 | public void setLongIn(final Object object, final int index, final long value) 130 | { 131 | final long offset = this.getFieldLocation(LONG_SIZE, index); 132 | UNSAFE.putLong(object, offset, value); 133 | } 134 | 135 | @Override 136 | public double readDoubleFrom(final Object object, final int index) 137 | { 138 | final long offset = this.getFieldLocation(DOUBLE_SIZE, index); 139 | return UNSAFE.getDouble(object, offset); 140 | } 141 | 142 | @Override 143 | public void setDoubleIn(final Object object, final int index, final double value) 144 | { 145 | final long offset = this.getFieldLocation(DOUBLE_SIZE, index); 146 | UNSAFE.putDouble(object, offset, value); 147 | } 148 | 149 | @Override 150 | public float readFloatFrom(final Object object, final int index) 151 | { 152 | final long offset = this.getFieldLocation(FLOAT_SIZE, index); 153 | return UNSAFE.getFloat(object, offset); 154 | } 155 | 156 | @Override 157 | public void setFloatIn(final Object object, final int index, final float value) 158 | { 159 | final long offset = this.getFieldLocation(FLOAT_SIZE, index); 160 | UNSAFE.putFloat(object, offset, value); 161 | } 162 | 163 | private long getFieldLocation(final long fieldSize, final int index) 164 | { 165 | return fieldSize * index + HEADER_SIZE; 166 | } 167 | 168 | static 169 | { 170 | try 171 | { 172 | final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); 173 | unsafeField.setAccessible(true); 174 | 175 | UNSAFE = (Unsafe) unsafeField.get(null); 176 | } 177 | catch (final Exception e) 178 | { 179 | throw new RuntimeException("Can't access Unsafe", e); 180 | } 181 | 182 | OOP_SIZE = HotSpotEstimator.estimateOopSize(UNSAFE); 183 | HEADER_SIZE = HotSpotEstimator.estimateHeaderSize(UNSAFE); 184 | 185 | System.out.println(MessageFormat.format("OOP_SIZE: {0}, HEADER_SIZE: {1}", OOP_SIZE, HEADER_SIZE)); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/main/java/pl/north93/arrays/jvm/MemoryAccess.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays.jvm; 2 | 3 | public interface MemoryAccess 4 | { 5 | Object instantiate(Class clazz); 6 | 7 | Object readObjectFrom(Object object, int index); 8 | 9 | void setObjectIn(Object object, int index, Object value); 10 | 11 | boolean readBooleanFrom(Object object, int index); 12 | 13 | void setBooleanIn(Object object, int index, boolean value); 14 | 15 | byte readByteFrom(Object object, int index); 16 | 17 | void setByteIn(Object object, int index, byte value); 18 | 19 | short readShortFrom(Object object, int index); 20 | 21 | void setShortIn(Object object, int index, short value); 22 | 23 | char readCharFrom(Object object, int index); 24 | 25 | void setCharIn(Object object, int index, char value); 26 | 27 | int readIntFrom(Object object, int index); 28 | 29 | void setIntIn(Object object, int index, int value); 30 | 31 | long readLongFrom(Object object, int index); 32 | 33 | void setLongIn(Object object, int index, long value); 34 | 35 | double readDoubleFrom(Object object, int index); 36 | 37 | void setDoubleIn(Object object, int index, double value); 38 | 39 | float readFloatFrom(Object object, int index); 40 | 41 | void setFloatIn(Object object, int index, float value); 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/pl/north93/arrays/BooleanArrayTest.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import pl.north93.arrays.impl.ArrayFactory; 7 | 8 | public class BooleanArrayTest 9 | { 10 | private final ArrayFactory arrayFactory = new ArrayFactory(); 11 | 12 | @Test 13 | public void createEmptyBooleanArray() 14 | { 15 | final BooleanArray array = this.arrayFactory.getBooleanArray(0); 16 | 17 | Assertions.assertEquals(0, array.size()); 18 | Assertions.assertThrows(ArrayIndexOutOfBoundsException.class, () -> array.get(1)); 19 | } 20 | 21 | @Test 22 | public void getAndSetValueFromBooleanArray() 23 | { 24 | final BooleanArray array = this.arrayFactory.getBooleanArray(1); 25 | 26 | Assertions.assertEquals(1, array.size()); 27 | 28 | array.setBoolean(0, true); 29 | Assertions.assertEquals(true, array.get(0)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/pl/north93/arrays/ObjectArrayTest.java: -------------------------------------------------------------------------------- 1 | package pl.north93.arrays; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import pl.north93.arrays.impl.ArrayFactory; 7 | 8 | public class ObjectArrayTest 9 | { 10 | private final ArrayFactory arrayFactory = new ArrayFactory(); 11 | 12 | @Test 13 | public void createEmptyObjectArray() 14 | { 15 | final Array array = this.arrayFactory.getObjectArray(Object.class, 0); 16 | 17 | Assertions.assertEquals(0, array.size()); 18 | Assertions.assertThrows(ArrayIndexOutOfBoundsException.class, () -> array.get(1)); 19 | } 20 | 21 | @Test 22 | public void getAndSetValueFromObjectArray() 23 | { 24 | final Array array = this.arrayFactory.getObjectArray(Object.class, 1); 25 | 26 | Assertions.assertEquals(1, array.size()); 27 | 28 | final Object object = new Object(); 29 | array.set(0, object); 30 | 31 | Assertions.assertEquals(object, array.get(0)); 32 | } 33 | } 34 | --------------------------------------------------------------------------------