Skip to content

Instantly share code, notes, and snippets.

@matklad
Last active September 26, 2016 09:05
Show Gist options
  • Save matklad/e891a99131b954c8e27e80dc4572aea4 to your computer and use it in GitHub Desktop.
Save matklad/e891a99131b954c8e27e80dc4572aea4 to your computer and use it in GitHub Desktop.
Random java memory allocation benchmark
λ ~/bench/ alias t="/run/current-system/sw/bin/time -f \"%es %MkB\" "
λ ~/bench/ javac -version
javac 1.8.0_101
λ ~/bench/ java -version
java version "1.8.0_101"
Java(TM) SE Runtime Environment (build 1.8.0_101-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)
λ ~/bench/ cat Strings.java
public class Strings {
public static void main(String[] args) {
String x = "ab";
String y = "zc";
int total = 0;
for (int i = 0; i < (int)1e8; i++) {
String s = x + y;
total += s.length();
}
System.out.println(total);
}
}
λ ~/bench/ javac Strings.java
λ ~/bench/ t java -Xmx64M -Xss64M Strings
400000000
2.22s 45420kB
λ ~/bench/ t java -Xmx128M -Xss128M Strings
400000000
2.17s 67748kB
λ ~/bench/ t java -Xmx256M -Xss256M Strings
400000000
2.16s 111340kB
λ ~/bench/ t java -Xmx512M -Xss512M Strings
400000000
2.21s 198036kB
λ ~/bench/ t java -Xmx1024M -Xss1024M Strings
400000000
2.33s 373104kB
λ ~/bench/ cat Point1.java
class Point1 {
static class Point implements Comparable<Point> {
final int x;
final int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public int compareTo(Point o) {
return x*x ^ y*y - (o.x*o.x ^ o.y*o.y);
}
}
static Point maxPoint() {
Point ans = new Point(0, 0);
int a = 92;
int b = 62;
for (int i = 0; i < (int)1e9; i++) {
a = a*123 + 798723497;
b = b*961 + 123573489;
Point next = new Point(a, b);
if (0 < next.compareTo(ans)) {
ans = next;
}
}
return ans;
}
public static void main(String[] args) {
System.out.println(maxPoint().x);
}
}
λ ~/bench/ javac Point1.java
λ ~/bench/ t java -Xmx64M -Xss64M Point1
501891369
8.76s 44564kB
λ ~/bench/ t java -Xmx256M -Xss256M Point1
501891369
7.88s 110380kB
λ ~/bench/ t java -Xmx1024M -Xss1024M Point1
501891369
7.81s 372180kB
λ ~/bench/ cat Point2.java
class Point2 {
static class Point implements Comparable<Point> {
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public int compareTo(Point o) {
return x*x ^ y*y - (o.x*o.x ^ o.y*o.y);
}
}
static Point maxPoint() {
Point ans = new Point(0, 0);
int a = 92;
int b = 62;
for (int i = 0; i < (int)1e9; i++) {
a = a*123 + 798723497;
b = b*961 + 123573489;
Point next = new Point(a, b);
if (0 < next.compareTo(ans)) {
ans.x = next.x;
ans.y = next.y;
}
}
return ans;
}
public static void main(String[] args) {
System.out.println(maxPoint().x);
}
}
λ ~/bench/ t java -Xmx64M -Xss64M Point2
501891369
9.32s 33232kB
λ ~/bench/ t java -Xmx256M -Xss256M Point2
501891369
9.31s 33232kB
λ ~/bench/ t java -Xmx1024M -Xss1024M Point2
501891369
9.34s 33240kB
λ ~/bench/ cat Point3.java
class Point3 {
static class Point implements Comparable<Point> {
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public int compareTo(Point o) {
return x*x ^ y*y - (o.x*o.x ^ o.y*o.y);
}
}
static Point maxPoint() {
Point ans = new Point(0, 0);
Point next = new Point(0, 0);
int a = 92;
int b = 62;
for (int i = 0; i < (int)1e9; i++) {
a = a*123 + 798723497;
b = b*961 + 123573489;
next.x = a;
next.y = b;
if (0 < next.compareTo(ans)) {
ans.x = next.x;
ans.y = next.y;
}
}
return ans;
}
public static void main(String[] args) {
System.out.println(maxPoint().x);
}
}
λ ~/bench/ javac Point3.java
λ ~/bench/ t java -Xmx64M -Xss64M Point3
501891369
8.70s 24560kB
λ ~/bench/ t java -Xmx256M -Xss256M Point3
501891369
8.70s 24592kB
λ ~/bench/ t java -Xmx1024M -Xss1024M Point3
501891369
8.71s 24764kB
@matklad
Copy link
Author

matklad commented Sep 25, 2016

Анализ

strings.txt

Программа делает O(n) аллокаций. В каждый момент времени достаточно O(1) памяти, однако она не освобождается, пока свободной памяти не станет мало. Т.е, используемая память не константа, а пропорциональна памяти, выделенной под JVM при старте.

ponit1.txt

То же самое, что и strings.txt

point2.txt

Как point1, но позволяет JVM провести escape analysis и аллоцировать next на стеке. Используемая память константна и не зависит от флагов JVM. Время allegedly больше чем у point1.

point3.txt

Как point2, но явно переиспользует один и то же объект. Используемая память также константна. Время allegedly больше чем у point1.

Выводы.

  1. JVM это газ, который заполняет всю выделенную память (это не так для старых jvm с флагом -client).
  2. Escape analysis это не миф.
  3. Аллоцировать кучу маленьких иммутабельных объектов быстрее (не медленнее), чем не аллоцировать и in-place мутировать (WTF?!). Аллокация объекта в Java это инкремент указателя, и это сильно дешевле, чем аллокация в C++.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment