/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Loic Le Coq
* Copyright (C) 2013 Marko Zivkovic
*
* Contact Information: marko88zivkovic at gmail dot com
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version. This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details. You should have received a copy of the
* GNU General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*
* This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
* during his Bachelor thesis at the computer science department of ETH Zurich,
* in the year 2013 and/or during future work.
*
* It is a reengineered version of XLogo written by Loic Le Coq, published
* under the GPL License at http://xlogo.tuxfamily.org/
*
* Contents of this file were initially written by Loic Le Coq,
* a lot of modifications, extensions, refactorings have been applied by Marko Zivkovic
*/
/**
* Title : XLogo
* Description : XLogo is an interpreter for the Logo
* programming language
*
* @author Loïc Le Coq
**/
package xlogo;
import xlogo.messages.async.dialog.DialogMessenger;
import xlogo.storage.global.GlobalConfig;
/**
* This class is a thread that prevents from memory Overflow
* Those problems could happen when a program loops indefinitely
* Eg:
*
*
* To bad
* fd 1 rt 1
* bad
* this lines will explode memory
* end
*
*
* @author loic
*
* @author Marko Zivkovic - Extensive tests and provocation of the memory usage error message have shown
* that in XLogo4Schools, using a maximum 128MB heap, the memory threshold is never reached
* (Or very, very late in a gigantic recursion).
*
*/
public class MemoryChecker extends Thread
{
/**
* The main frame
*/
private Application cadre;
/**
* This boolean indicates if the thread has to continue.
* If false, the thread will stop.
*/
private boolean alive;
/**
* Constructs the Memory Checker for the main Frame
*
* @param cadre
* the main Frame
*/
public MemoryChecker(Application cadre)
{
this.cadre = cadre;
}
private static int maxSleepTime = 10000;
private static int minSleepTime = 500;
private static int sleepRange = maxSleepTime - minSleepTime;
/**
* The Run Method for the Thread
*/
public void run()
{
/*
* Marko : I reduced the amount of calculations done in every iteration.
* Before it included 1 subtraction and 2 divisions and a fetch from GlobalConfig,
* now only 1 subtraction is used to determine consumed > consumptionThreshold
* and another long comparison was added to regulate frequency of memory checks.
*/
long consumptionThreshold = GlobalConfig.getMemoryThreshold();
long listenThreshold = (long) (0.8 * consumptionThreshold);
/*
* I increased default sleeping time to about 10 seconds, because a check every 1 second is too much overhead for doing nothing useful.
* However, as soon as the listenThreshold is reached, the sleepTime will start to decrease.
* Only once the listenThreshold is reached,
* the MemoryChecker becomes important to prevent an OutOfMemoryError and start suggesting the garbage collector to work.)
*/
int sleepTime = maxSleepTime;
alive = true;
while (alive)
{
try
{
Thread.sleep(sleepTime);
}
catch (InterruptedException ignore)
{}
long free = Runtime.getRuntime().freeMemory();
long total = Runtime.getRuntime().totalMemory();
long consumed = (total - free);
if (consumed >= listenThreshold)
{
sleepTime = (int) (maxSleepTime - consumed / total * sleepRange);
/*
* Marko :
* In XLogo, when the error dialog was displayed, it usually kept displaying several times, disrupting any further use of the interpreter.
* The only out was to restart the application. This indicates that, although heavy memory usage was detected by this thread,
* the garbage collector did not yet start to free memory from dead objects.
* (Discussions about GC in the Internet indicate that the Java GC starts working as soon as a heap generation gets full.
* It is possible that the young generation is not full enough when 0.9*MaxMemory is reached, thus the message keeps appearing all the time,
* because memory is not sufficiently cleaned up.)
* Therefore, at least suggest the GC that it would be a good moment for garbage collection, and help recover. That's the best we can do.
*/
System.gc();
}
else
sleepTime = maxSleepTime;
if (consumed > consumptionThreshold)
{
cadre.error = true;
alive = false;
DialogMessenger.getInstance().dispatchError(Logo.messages.getString("erreur"),
Logo.messages.getString("depassement_memoire"));
}
}
}
/**
* Causes the memory checker loop to break.
* @author Marko
*/
public void kill()
{
alive = false;
}
}