I was trying to study different exceptions in Java and came across the OutOfMemoryError and I wanted to see it in work so I wrote the following program to create infinite objects by creating them in a infinite loop. The program does go in infinite loop it does not throw the OutOfMemoryError exception.
class Test {
public static void main(String...args) {
while(true) {
Integer i = new Integer();
}
}
}
You are on the right track. The only thing you are missing is the concept of garbage collection. The program does infact create infinite Integer objects but after the 1st iteration, the object created in the previous iteration becomes eligible for GC.
Consider this:
Integer i;
i = new Integer(); // 1. create new object and make reference variable i refer to it.
i = new Integer(); // 2. create another object and make reference variable i refer to it...there is no way to get to the object created in step1 so obj in step 1 is eligible for GC.
If you want to see the OutOfMemoryError you need to somhow make sure that there is a way to get to the objects created in the infinite loop. So you can do something like:
class Test {
public static void main(String...args) {
Vector v = new Vector(); // create a new vector.
while(true) {
v.addElement(new Integer(1)); // create a new Integer and add it to vector.
}
}
}
In this program Integer objects are created infinitely as before but now I add them to a vector so ensure that there is a way to get to them and they do not become GC eligible.
Your variable i is garbage collected before you hit the OutOfMemoryError, since it isn't used anymore.
The easiest way to get an OutOfMemoryException is to create an array which doesn't fit into memory:
public class TestOutOfMemoryException
{
public static void main(String[] args)
{
final long maxMemory = Runtime.getRuntime().maxMemory();
System.out.println(maxMemory);
final byte[] boom = new byte[(int) maxMemory];
}
}
The variable i is only scoped to the loop, it doesn't survive past each iteration. Thus it is being garbage collected before the program can run out of memory. Try creating an ArrayList before entering the loop and adding each instance of i to it:
public class Test {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
while (true) {
Integer i = new Integer();
list.add(i);
}
}
}
try with something like a
List l = new ArrayList();
int i = 0;
while(true) { l.add(i++); }
Related
I always thought that a while (true) {...Any code..} would always result in a out of memory error.
But as I go deeper in java it seems it might not be like that.
I'm not able to confirm but if we have a while true that only does calculations, We are not expected to have an out of memory error, only a very detrimental cpu performance, right?
On the other hand if we are always requiring more memory it is expected to have a out of memory error.
I've 3 cases below.
calculations only (I think no memory is being allocated under the hood)
Ever increasing arraylist which it looks an obvious out of memory error
always instanting arraylist with new keyword. I dont know if it causes an out of memory error, because of garbage collector.
I'm not testing im my pc because I only have one, hope someone has the knowledge.
Code
import java.util.*;
public class HelloLeak{
//calculations only, MemoryLeak?
public static void outofmemo1(){
long i = 0;
while (true)
{
i = i * i;
}
}
//adding infinite number of objects, memory leak confirmed.
public static void outofmemo2(){
int i = 0;
List<Integer> l = new ArrayList<>();
while (true)
{
l.add(i);
}
}
//Creating infinite number of ArrayList objects, will garbage collector clear the unused objects or we will get memory leak?
public static void outofmemo3(){
List<Integer> l = new ArrayList<>();
while (true)
{
l = new ArrayList<>();
}
}
public static void main(String []args){
outofmemo1();
//outofmemo2();
//outofmemo3();
}
}
Will do absolutly nothing except ending in an endless loop.
Will crash with an OutOfMemoryError, because you add always a new element to the list, until the heap is filled.
Will be like 1. but you may have spikes up to for example 2GB, then the GC will come, see that there are unused objects, removes them. After that it will spike again, and so on
class Testing{
static int count = 0;
public Testing(){
count++;
}
public static void main(String[] args){
Testing[] testObjects= new Testing[20];
for(int i = 1; i<20;i++){
if(Testing.count==5){
System.out.println("5 objects are created.. Can't create anymore, although 20 objects can be stored");
System.exit(0);
}
else{
testObjects[i] = new Testing();
System.out.println("Object "+Testing.count+" is created.");
}
} // for loop close
System.out.println("Program will exit");
}
}
I have tried this code but it only keeps the information of a single run and I need to track the multiple execution
Declaring count as static binds that variable not to a class instance, but to the class itself. Therefore, all objects share a single count variable. The way you have your code set up, where you increment count in each constructor call, keeps track of how many Testing objects have been created throughout the lifetime of this program. If you want to persist data, you'll need to look at the Preferences class. Essentially, at the end of your program you'd put the value into storage:
myPreferences.put("ObjectsCreated", Testing.count);
Then retireve it later with
int previous = myPreferences.getInt("ObjectsCreated", 0);
You have to persist the value of count, for example to a file.
I have found a strange behaviour in my java program here is my code looks like this
public class JavaTest {
private final int dataSize = (int) (Runtime.getRuntime().maxMemory() * 0.6);
public void test() {
{
System.out.println(dataSize);
byte[] data = new byte[dataSize];
}
// for (int i = 0; i < 10; i++) {
// System.out.println("Please be so kind and release memory");
// }
System.out.println(dataSize);
byte[] data2 = new byte[dataSize];
}
public static void main(String[] args) {
JavaTest jmp = new JavaTest();
jmp.test();
}
}
Here when I am commenting the for loop I am getting Exception in thread "main" java.lang.OutOfMemoryError: Java heap space that i can understand the jvm heap sapce is full.
But with that for loop in my code, it executes properly. How comes?
I think it's because you declare byte[] data inside { } block, which means data's scope ends when the code block ends. With loop uncommented, you are probably giving time to garbage collector to free the memory taken by data. And when you comment out the loop, GC doesn't have time to free up that memory yet.
If you remove { } around data declaration, it will also throw OutOfMemoryException even with loop uncommented.
UPDATE
This blog post stated in the comments by #SubOptimal proves this theory wrong, looks like it doesn't have anything to do with time needed by GC to free the memory. I'll quote relevant parts from the blog
The majority of responses were incorrect and suggested that the for() loop either gave the GC time to do its work during the System.out.println()...
Some of my readers realised that it had nothing to do with the System.out.println and that a simple int i = 0; would suffice. If you declare any local variable immediately after the code block, you break the strong reference to the byte[] held in the stack frame 1 before you invoke the new byte[] the second time.
Out of curiosity, I measured the performance between static block and static method initializer. First, I implemented the above mentioned methods in two separate java classes, like so:
First:
class Dummy {
static java.util.List<Integer> lista = new java.util.ArrayList<Integer>();
static {
for(int i=0; i < 1000000; ++i) {
lista.add(new Integer(i));
}
}
}
public class First {
public static void main(String[] args) {
long st = System.currentTimeMillis();
Dummy d = new Dummy();
long end = System.currentTimeMillis() - st;
System.out.println(end);
}
}
Second:
class Muddy {
static java.util.List<Integer> lista = new java.util.ArrayList<Integer>();
public static void initList() {
for(int i=0; i < 1000000; ++i) {
lista.add(new Integer(i));
}
}
}
public class Second {
public static void main(String[] args) {
long st = System.currentTimeMillis();
Muddy.initList();
Muddy m = new Muddy();
long end = System.currentTimeMillis() - st;
System.out.println(end);
}
}
Then I executed this little batch script to measure it 100 times and put the values in a file. batchFile.bat First Second dum.res.txt
After that, I wrote this piece of code to calculate mean value and standard deviation of Dummy's and Muddy's measured values.
This is the result that I've got:
First size: 100 Second size: 100
First Sum: 132 Std. deviation: 13
Second Sum: 112 Std. deviation: 9
And it is similar on my other machines...every time I test it.
Now I'm wondering, why is it so? I checked the bytecode and Second.class has one instruction more (call to static initList()) between calls to System.currentTimeMillis().
They both do the same thing, but why is the First one slower? I can't really reason it out just by looking at the bytecode, since this was my first time touching javap; I don't understand bytecode yet.
I think that the reason why the static block version is slower than the static method version could be due to the different JIT optimization that they get ...
See this interesting article for more interesting information : Java Secret: Are static blocks interpreted?
Here's my guess as to the reason for this:
The initialization you are doing is creating enough objects that it is causing one or more garbage collections.
When the initialization is called from the static block, it is done during the class initialization rather than during simple method execution. During class initialization, the garbage detector may have a little more work to do (because the execution stack is longer, for example) than during simple method execution, even though the contents of the heap are almost the same.
To test this, you could try adding -Xms200m or something to your java commands; this should eliminate the need to garbage collect during the initialization you are doing.
I was reading about CopyOnWriteArrayList and was wondering how can I demonstrate data race in ArrayList class. Basically I'm trying to simulate a situation where ArrayList fails so that it becomes necessary to use CopyOnWriteArrayList. Any suggestions on how to simulate this.
A race is when two (or more) threads try to operate on shared data, and the final output depends on the order the data is accessed (and that order is indeterministic)
From Wikipedia:
A race condition or race hazard is a flaw in an electronic system or process whereby the output and/or result of the process is unexpectedly and critically dependent on the sequence or timing of other events. The term originates with the idea of two signals racing each other to influence the output first.
For example:
public class Test {
private static List<String> list = new CopyOnWriteArrayList<String>();
public static void main(String[] args) throws Exception {
ExecutorService e = Executors.newFixedThreadPool(5);
e.execute(new WriterTask());
e.execute(new WriterTask());
e.execute(new WriterTask());
e.execute(new WriterTask());
e.execute(new WriterTask());
e.awaitTermination(20, TimeUnit.SECONDS);
}
static class WriterTask implements Runnable {
#Override
public void run() {
for (int i = 0; i < 25000; i ++) {
list.add("a");
}
}
}
}
This, however, fails when using ArrayList, with ArrayIndexOutOfbounds. That's because before insertion the ensureCapacity(..) should be called to make sure the internal array can hold the new data. And here's what happens:
the first thread calls add(..), which in turn calls ensureCapacity(currentSize + 1)
before the first thread has actually incremented the size, the 2nd thread also calls ensureCapacity(currentSize + 1).
because both have read the initial value of currentSize, the new size of the internal array is currentSize + 1
the two threads make the expensive operation to copy the old array into the extended one, with the new size (which cannot hold both additions)
Then each of them tries to assign the new element to array[size++]. The first one succeeds, the second one fails, because the internal array has not been expanded properly, due to the rece condition.
This happens, because two threads have tried to add items at the same time on the same structure, and the addition of one of them has overridden the addition of the other (i.e. the first one was lost)
Another benefit of CopyOnWriteArrayList
multiple threads write to the ArrayList
a thread iterates the ArrayList. It will surely get ConcurrentModificationException
Here's how to demonstrate it:
public class Test {
private static List<String> list = new ArrayList<String>();
public static void main(String[] args) throws Exception {
ExecutorService e = Executors.newFixedThreadPool(2);
e.execute(new WriterTask());
e.execute(new ReaderTask());
}
static class ReaderTask implements Runnable {
#Override
public void run() {
while (true) {
for (String s : list) {
System.out.println(s);
}
}
}
}
static class WriterTask implements Runnable {
#Override
public void run() {
while(true) {
list.add("a");
}
}
}
}
If you run this program multiple times, you will often be getting ConcurrentModificationException before you get OutOfMemoryError.
If you replace it with CopyOnWriteArrayList, you don't get the exception (but the program is very slow)
Note that this is just a demonstration - the benefit of CopyOnWriteArrayList is when the number of reads vastly outnumbers the number of writes.
Example:
for (int i = 0; i < array.size(); ++i) {
Element elm = array.get(i);
doSomethingWith(elm);
}
If another thread calls array.clear() before this thread calls array.get(i), but after it has compared i with array.size(), -> ArrayIndexOutOfBoundsException.
Two threads, one incrementing the arraylist and one decrementing. Data race could happen here.