Java JNA get desktop item position - java

Goal
Obtaining the desktop coordinates of a desktop item/icon.
Attempt
I have obtained the SysListView32 window handle, which contains the desktop icons using:
HWND hWnd_Progman = User32.INSTANCE.FindWindow("Progman", "Program Manager");
HWND hWnd_SHELLDLL_DefView = User32.INSTANCE.FindWindowEx(hWnd_Progman, null, "SHELLDLL_DefView", null);
HWND hWnd_SysListView32 = User32.INSTANCE.FindWindowEx(hWnd_SHELLDLL_DefView, null, "SysListView32", "FolderView");
I have obtained the desktop item count:
LRESULT result = User32.INSTANCE.SendMessage(hWnd_SysListView32, LVM_GETITEMCOUNT, new WPARAM(), new LPARAM());
long desktopIconCount = result.longValue();
I have SET a desktop item position (validating that SysListView32 is the right list view for desktop items). The passed x and y coordinates corresponded with the offset from the left-top of my left-most monitor to the left-top corner of the desktop item. Code:
int itemIndex = 0; // Allows 0 to desktopIconCount - 1.
int x = ...;
int y = ...;
LRESULT res = User32.INSTANCE.SendMessage(hWnd_SysListView32, LVM_SETITEMPOSITION, new WPARAM(itemIndex), new LPARAM((x & 0xFFFF) | (y << 16)));
Now, to get a desktop item position, one needs to send LVM_GETITEMPOSITION to the SysListView32 and include a pointer to an address where it can write the position to. However, this pointer has to be a valid address in the memory of the process belonging to SysListView32. So what I tried to do is to:
Get the process belonging to SysListView32.
Allocate memory in that process.
Write a POINT object in this memory (used for the item position).
Send LVM_GETITEMPOSITION to SysListView32 with a pointer to this allocated memory.
Read this POINT object from memory. At this point the process should have written the desktop item position to it.
I've tried this with the following code:
// Get the SysListView32 process handle.
IntByReference processIdRef = new IntByReference();
User32.INSTANCE.GetWindowThreadProcessId(hWnd_SysListView32, processIdRef);
HANDLE procHandle = Kernel32.INSTANCE.OpenProcess(
Kernel32.PROCESS_VM_OPERATION | Kernel32.PROCESS_VM_WRITE | Kernel32.PROCESS_VM_READ,
false, processIdRef.getValue());
// Allocate memory in the SysView32 process.
int pointSize = Native.getNativeSize(POINT.class)); // 8 bytes.
LPVOID pMem = MyKernel32.INSTANCE.VirtualAllocEx(procHandle, new LPVOID(), new SIZE_T(pointSize),
MyKernel32.MEM_COMMIT, MyKernel32.PAGE_READWRITE);
// Put some POINT-sized object in the process its memory.
boolean success = Kernel32.INSTANCE.WriteProcessMemory(
procHandle, pMem.getPointer(), pMem.getPointer(), pointSize, null);
if(!success) {
System.out.println("Write error = " + Kernel32.INSTANCE.GetLastError());
System.exit(1);
}
// Send the LVM_GETITEMPOSITION message to the SysListView32.
int itemIndex = 0; // Allows 0 to desktopIconCount - 1.
LRESULT res = MyUser32.INSTANCE.SendMessage(
hWnd_SysListView32, LVM_GETITEMPOSITION, new WPARAM(itemIndex), pMem.getPointer());
System.out.println("Message result = " + res.longValue());
// Read the earlier POINT-sized written memory.
POINT point = new POINT();
success = Kernel32.INSTANCE.ReadProcessMemory(
procHandle, pMem.getPointer(), point.getPointer(), pointSize, null);
if(!success) {
System.out.println("Read error = " + Kernel32.INSTANCE.GetLastError());
System.exit(1);
}
System.out.println("Point found: x=" + pos.x + ", y=" + pos.y);
Here, MyUser32 is created as follows:
interface MyUser32 extends User32 {
static MyUser32 INSTANCE =
(MyUser32) Native.load("user32", MyUser32.class, W32APIOptions.DEFAULT_OPTIONS);
LRESULT SendMessage(HWND hWnd, int msg, WPARAM wParam, Pointer pointer);
}
and MyKernel32 is created as follows:
interface MyKernel32 extends Kernel32 {
static final MyKernel32 INSTANCE =
(MyKernel32) Native.load("kernel32", MyKernel32.class, W32APIOptions.DEFAULT_OPTIONS);
static int MEM_COMMIT = 0x1000;
static int PAGE_READWRITE = 0x04;
LPVOID VirtualAllocEx(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, int flAllocationType, int flProtect);
}
To be complete, the following additional static values are used:
static final int LVM_FIRST = 0x1000;
static final int LVM_GETITEMCOUNT = LVM_FIRST + 4;
static final int LVM_SETITEMPOSITION = LVM_FIRST + 15;
static final int LVM_GETITEMPOSITION = LVM_FIRST + 16;
Problem
The WriteProcessMemory call often fails with error code 299 ERROR_PARTIAL_COPY and even when it does not fail, the returned POINT is always (0,0). I expect the problem to be in either the SendMessage / VirtualAllocEx method declarations in MyUser32 or MyKernel32, or by me not understanding properly which object/pointer to pass to VirtualAllocEx or WriteProcessMemory.
I've done a lot of research and figured out how it should work in C/C++, but I could not find any working code example using JNA for my case.
Thank you for showing interest and trying to help if you made it all the way through my message.

The problem is that com.sun.jna.Native.getNativeSize(Class) is not the right function to use in this case. The problem is visible, when using a 32bit JVM (it is not visible a 64bit VM).
For structures the above mentioned function assumes that they are passed by reference (pointer to the structure) and thus the function returns the value of Native.POINTER_SIZE. On a 64Bit VM, this matches by luck the size of the POINT structure. On a 32Bit VM Native.POINTER_SIZE is 4 byte, and so can only hold parts of the result structure.
The most relevant parts: to determine the size of a Structure in JNA, use the Structure#size function. In this case it is also helpful to use the final parameter of ReadProcessMemory. The function returns the number of bytes that were read and shows the difference (4 vs. 8).
Further comments: remember to free the memory you allocated and also close the process handle to received.
Here is the full runnable sample (only missing imports, tested with JNA 5.2):
public class Test {
private interface Kernel32 extends com.sun.jna.platform.win32.Kernel32 {
Kernel32 INSTANCE = Native.load("kernel32", Kernel32.class, W32APIOptions.DEFAULT_OPTIONS);
public Pointer VirtualAllocEx(HANDLE hProcess, Pointer lpAddress, SIZE_T dwSize, int flAllocationType, int flProtect);
public boolean VirtualFreeEx(HANDLE hProcess, Pointer lpAddress, SIZE_T dwSize, int dwFreeType);
int MEM_COMMIT = 0x00001000;
int MEM_RESERVE = 0x00002000;
int MEM_RESET = 0x00080000;
int MEM_RESET_UNDO = 0x1000000;
int MEM_LARGE_PAGES = 0x20000000;
int MEM_PHYSICAL = 0x00400000;
int MEM_TOP_DOWN = 0x00100000;
int MEM_COALESCE_PLACEHOLDERS = 0x00000001;
int MEM_PRESERVE_PLACEHOLDER = 0x00000002;
int MEM_DECOMMIT = 0x4000;
int MEM_RELEASE = 0x8000;
}
private static final int LVM_FIRST = 0x1000;
private static final int LVM_GETITEMCOUNT = LVM_FIRST + 4;
private static final int LVM_GETITEMPOSITION = LVM_FIRST + 16;
public static void main(String[] args) throws IOException, InterruptedException {
// Find the HWND for the "desktop" list view
HWND hWnd_Progman = User32.INSTANCE.FindWindow("Progman", "Program Manager");
HWND hWnd_SHELLDLL_DefView = User32.INSTANCE.FindWindowEx(hWnd_Progman, null, "SHELLDLL_DefView", null);
HWND hWnd_SysListView32 = User32.INSTANCE.FindWindowEx(hWnd_SHELLDLL_DefView, null, "SysListView32", "FolderView");
// Fetch the icon count
int itemCount = User32.INSTANCE.SendMessage(hWnd_SysListView32, LVM_GETITEMCOUNT, new WPARAM(), new LPARAM()).intValue();
System.out.println("Desktop Icons: " + itemCount);
// Get the SysListView32 process handle.
IntByReference processIdRef = new IntByReference();
User32.INSTANCE.GetWindowThreadProcessId(hWnd_SysListView32, processIdRef);
HANDLE procHandle = Kernel32.INSTANCE.OpenProcess(
Kernel32.PROCESS_VM_OPERATION | Kernel32.PROCESS_VM_WRITE | Kernel32.PROCESS_VM_READ,
false, processIdRef.getValue());
// Allocate memory in the SysView32 process.
int pointSize = new POINT().size(); // 8 bytes.
Pointer pMem = Kernel32.INSTANCE.VirtualAllocEx(procHandle, null, new SIZE_T(pointSize),
Kernel32.MEM_COMMIT, Kernel32.PAGE_READWRITE);
for (int i = 0; i < itemCount; i++) {
// Send the LVM_GETITEMPOSITION message to the SysListView32.
LRESULT res = User32.INSTANCE.SendMessage(
hWnd_SysListView32, LVM_GETITEMPOSITION, new WPARAM(i), new LPARAM(Pointer.nativeValue(pMem)));
if(res.intValue() != 1) {
throw new IllegalStateException("Message sending failed");
}
// Read the earlier POINT-sized written memory.
POINT point = new POINT();
IntByReference read = new IntByReference();
boolean success = Kernel32.INSTANCE.ReadProcessMemory(
procHandle, pMem, point.getPointer(), pointSize, read);
if (!success) {
System.out.println("Read error = " + Kernel32.INSTANCE.GetLastError());
System.exit(1);
}
point.read();
System.out.println("Point found: x=" + point.x + ", y=" + point.y);
}
// Release allocated memory
Kernel32.INSTANCE.VirtualFreeEx(procHandle, pMem, new SIZE_T(0), Kernel32.MEM_RELEASE);
// Close Process Handle
Kernel32.INSTANCE.CloseHandle(procHandle);
}
}

Related

Posting to a POSIX semaphore using JNA

I am trying to post to a semaphore using JNA on a Linux machine. For some reason, I always receive a 22 error (invalid argument) even for this simple example. In my understanding, should the below code not open a POSIX semaphore, post to it and close it again?
public class Sample {
private static final int O_CREAT = 0x40;
public static void main(String[] args) throws Exception {
File notifier = new File("/tmp", "_test" + new Random().nextInt());
if (!notifier.isFile() && !notifier.createNewFile()) {
throw new IllegalStateException("Could not create notifier: " + notifier);
}
SempahoreLibrary library = Native.load("c", SempahoreLibrary.class);
Pointer semaphore = library.sem_open(notifier.getAbsolutePath(), O_CREAT, 666, 0);
try {
library.sem_post(semaphore);
} finally {
library.sem_close(semaphore);
}
}
interface SempahoreLibrary extends Library {
Pointer sem_open(String name, int flags, int mode, int value) throws LastErrorException;
int sem_post(Pointer pointer) throws LastErrorException;
int sem_close(Pointer pointer) throws LastErrorException;
}
}
I initially couldn't make it work with JNR either (strongly recommended over JNA), and got curious. Writing it in C helped.. :)
An strace on the C port made it clear you don't have to create a file upfront and
then "map" the semaphore to it. Also using the full path is wrong, because
semaphores are created in /dev/shm and the "/" in the path screws
up everything:
futex(0x7f731b1190d0, FUTEX_WAKE_PRIVATE, 2147483647) = 0
openat(AT_FDCWD, "/dev/shm/sem.sema", O_RDWR|O_NOFOLLOW) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=32, ...}) = 0
So you should be able to remove the whole file/path creation and just use a regular non-path name for the semaphore in sem_open. Also the file mode should be octal,
and you should make sure to also load the pthread library - it's required.
Here is a working example in C:
// clang -Wall sema.c -lpthread
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(int argc, char** argv)
{
sem_t* s = sem_open("notifier", O_CREAT, 0644, 0);
if (!s) {
perror("sem_open");
exit(errno);
}
printf("s: %p\n", s);
sem_post(s);
int value = -1;
sem_getvalue(s, &value);
printf("value: %d\n", value);
sem_wait(s);
sem_getvalue(s, &value);
printf("value: %d\n", value);
sem_close(s);
exit(EXIT_SUCCESS);
}
Here is a working Java version using JNR:
import jnr.ffi.LastError;
import jnr.ffi.LibraryLoader;
import jnr.ffi.Pointer;
import jnr.ffi.Runtime;
public class Semaphore
{
private static final int O_CREAT = 0x40;
public interface SempahoreLibrary
{
Pointer sem_open(String name, int flags, int mode, int value);
int sem_post(Pointer pointer);
int sem_close(Pointer pointer);
}
public static void main(String[] args) throws Exception
{
LibraryLoader<SempahoreLibrary> loader = LibraryLoader.create(SempahoreLibrary.class);
loader.library("c");
loader.library("pthread");
SempahoreLibrary library = loader.load();
jnr.ffi.Runtime runtime = Runtime.getRuntime(library);
Pointer semaphore = library.sem_open("notifier", O_CREAT, 0644, 0);
if (semaphore == null)
{
int errno = LastError.getLastError(runtime);
System.out.println("sem_open: " + errno);
System.exit(errno);
}
System.out.println("semaphore: " + Long.toHexString(semaphore.address()));
try
{
int error = library.sem_post(semaphore);
System.out.println("post: " + (error == 0 ? "OK" : LastError.getLastError(runtime)));
}
finally
{
int error = library.sem_close(semaphore);
System.out.println("close: " + (error == 0 ? "OK" : LastError.getLastError(runtime)));
}
}
}

JVM Bytecode, how can I find the type of local variables?

I'm working on a fork of FernFlower from Jetbrains and I've been adding minor improvements to it.
One thing that really annoys me about FernFlower is that it bases the type of the local variable based on its value in bpush/spush etc. While Jode and Procyon somehow find a way to find the original value of a local variable.
Here is the original source code.
public static void main(String[] args) throws Exception {
int hello = 100;
char a2 = 100;
short y1o = 100;
int hei = 100;
System.out.println(a2+" "+y1o+", "+hei+", "+hello);
}
When decompiled with FernFlower, it outputs this:
public static void main(String[] args) throws Exception {
byte hello = 100;
char a2 = 100;
byte y1o = 100;
byte hei = 100;
System.out.println(a2 + " " + y1o + ", " + hei + ", " + hello);
}
But when decompiled with Jode/Procyon it outputs the original local variable types:
public static void main(String[] args)
throws Exception
{
int hello = 100;
char a2 = 'd';
short y1o = 100;
byte hei = 100;
System.out.println(a2 + " " + y1o + ", " + hei + ", " + hello);
}
I was wondering how is this possible because I thought no local variable type information is stored at compile time? How can I add the same functionality to FernFlower?
.class files optionally contain a 'LocalVariableTable' attribute for debugging purposes. If you invoke the command javap -l <Class>.class you can see the data if it is present.
So after looking around and debugging I found that for some reason FernFlower decides to completely ignore some of the data in the LocalVariableTable.
Here is ferns original code for decoding the LocalVariableTable:
public void initContent(ConstantPool pool) throws IOException {
DataInputFullStream data = stream();
int len = data.readUnsignedShort();
if (len > 0) {
mapVarNames = new HashMap<Integer, String>(len);
for (int i = 0; i < len; i++) {
data.discard(4);
int nameIndex = data.readUnsignedShort();
data.discard(2);
int varIndex = data.readUnsignedShort();
mapVarNames.put(varIndex, pool.getPrimitiveConstant(nameIndex).getString());
}
} else {
mapVarNames = Collections.emptyMap();
}
}
If you want type information you need to add the following:
#Override
public void initContent(ConstantPool pool) throws IOException {
DataInputFullStream data = stream();
int len = data.readUnsignedShort();
if (len > 0) {
mapVarNames = new HashMap<Integer, String>(len);
mapVarTypes = new HashMap<Integer, String>(len);
for (int i = 0; i < len; i++) {
int start = data.readUnsignedShort();
int end = start + data.readUnsignedShort();
int nameIndex = data.readUnsignedShort();
int typeIndex = data.readUnsignedShort();
int varIndex = data.readUnsignedShort();
mapVarNames.put(varIndex, pool.getPrimitiveConstant(nameIndex).getString());
mapVarTypes.put(varIndex, pool.getPrimitiveConstant(typeIndex).getString());
}
} else {
mapVarNames = Collections.emptyMap();
mapVarTypes = Collections.emptyMap();
}
}
It now outputs the same code as Jode with proper variable types :)
I wonder why FernFlower chose to ignore this information.

Create JVM crashs outside from Visual Studio

i have following Problem. I write a little Application , that creates a Java Virtual Machine. If I start this programm inside of Visual Studio it works fine. But if i start it outside of visual studio the programm does not work and i have a ntdll.dll crash.
Here is my Code :
int result = 0;
LoadRuntimeLibrary(libPath);
// Load the JVM library
g_jniLibrary = LoadLibrary(libPath);
if (g_jniLibrary == NULL) {
info->Error("Could not load libary: ");
return -1;
}
// Grab the create VM function address
JNI_createJavaVM createJavaVM = (JNI_createJavaVM)GetProcAddress(g_jniLibrary, "JNI_CreateJavaVM");
if (createJavaVM == NULL) {
info->Error("ERROR: Could not find JNI_CreateJavaVM function");
return -1;
}
// Count the vm args
int numVMArgs = -1;
while (vmArgs[++numVMArgs] != NULL) {}
// Add the options for exit and abort hooks
int numHooks = 0;
JavaVMOption* options = (JavaVMOption*)malloc((numVMArgs + numHooks) * sizeof(JavaVMOption));
for (int i = 0; i < numVMArgs; i++){
options[i].optionString = vmArgs[i];
options[i].extraInfo = 0;
}
// Setup hook pointers
options[numVMArgs].optionString = "abort";
options[numVMArgs].extraInfo = (void*)&AbortHook;
options[numVMArgs + 1].optionString = "exit";
options[numVMArgs + 1].extraInfo = (void*)&ExitHook;
JavaVMInitArgs init_args;
memset(&init_args, 0, sizeof(init_args));
init_args.version = JNI_VERSION_1_8;
init_args.options = options;
init_args.nOptions = numVMArgs + numHooks;
init_args.ignoreUnrecognized = JNI_FALSE;
result = createJavaVM(&jvm, &env, &init_args); // here is the crash
env = GetJNIEnv(false);
Init(env);
result = RunMainClass(env, mainCls, argc, javaargs);
jvm->DestroyJavaVM();
FreeLibrary(g_jniLibrary);
return result;
I hope you hava any idea , what could be wrong
You are accessing the options array out of bounds. It only contains numVMArgs elements, as numHooks is zero.
This of course leads to undefined behavior when you do
options[numVMArgs].optionString = "abort";
options[numVMArgs].extraInfo = (void*)&AbortHook;
options[numVMArgs + 1].optionString = "exit";
options[numVMArgs + 1].extraInfo = (void*)&ExitHook;
as the indexes numVMArgs and numVMArgs + 1 are out of bounds.

Java Application Working fine in Windows 7, Windows XP but not in Windows 2008 Server r2

I have this Java Swings Application developed by someone else which is delaying the execution on certain processes on Windows Server 2008 r2 32 Bit but it works perfectly in windows XP 32 & Windows 7 32 Bit.
I have confirmed that the application is running on the same JRE (32 Bit Version) as of the Windows XP & Windows 7.
Note:
I have tested the application for Delays in Windows XP and Windows 7 and have faced no such issue.
But Windows Server 2008 r2 (32 Bit Operating System) is giving delays in Application logs.
Here is the code which is delaying the transaction:
private Thread TID; // The Consumer thread
private Object q[]; // Queued Values for thread
private int nQueue; // Number of items in the Queue
private int dataStatus; // Status of the Queue
private long timeout;
private String myName;
private int MaxSize;
// legal Queue status values
final static int NONE = 0; // No data available
final static int NEW = 1; // New data available
final static int OVERRUN = 2; // Data overrun (queue overflow)
final static int SHUTDOWN = 3; // Data stream shutdown
DataItem(Thread t, String name, int Q, long timeo)
{
q = new Object[Q];
nQueue = 0;
TID = t;
timeout = timeo;
dataStatus = NONE;
myName = name;
MaxSize = Q;
}
DataItem(Thread t, String name, int Q)
{
this(t, name, Q, 10);
}
/** shorter version for a queue size of two (2) */
DataItem(Thread t, String name)
{
this(t, name, 2, 3000);
}
synchronized Object fetch(Trace log) throws DataStreamOverrun,
DataStreamShutdown,
DataStreamTimeout
{
Object r = null;
/* If nothing is waiting, we sleep.*/
if (dataStatus == NONE)
{
log.event("dataStatus :"+dataStatus);
try
{
long was = System.currentTimeMillis();
// wait();
log.event("was :"+was);
wait(timeout);
log.event("timeout :"+timeout);
if ((System.currentTimeMillis() - was) >= timeout)
throw new DataStreamTimeout();
}
catch (InterruptedException e){
log.event("SHUTDOWN :"+SHUTDOWN);
dataStatus = SHUTDOWN;
}
}
if(nQueue > 0)
{
log.event("--------- " + "Version ( " + MyPin.getVersion() +
" ) ---------");
log.event("Reading msg from stream : " + myName +
" size(" + nQueue + "/" + MaxSize + ")");
}
/* Data (or Status) has arrived so dispatch it. */
switch (dataStatus)
{
case NONE:
break;
case NEW:
r = q[0];
System.arraycopy(q, 1, q, 0, q.length - 1);
nQueue--;
break;
case OVERRUN:
dataStatus = NEW; // Next call will get the data
throw new DataStreamOverrun();
case SHUTDOWN:
throw new DataStreamShutdown();
}
if (nQueue == 0)
dataStatus = NONE;
return r;
}

Determine how much memory my app is using

I have written a java program, and I want to see when it runs how much RAM it uses. Is there any way to see how much RAM usage is related to my program? I mean something like time usage of the program that can be seen by writing this code before and after calling the main code:
long startTime = System.currentTimeMillis();
new Main();
long endTime = System.currentTimeMillis();
System.out.println("Total Time: " + (endTime - startTime));
You can use the following class. Implemeting the Instantiator interface you can execute several time the same process to get a precise view of the memory consumption
public class SizeOfUtil {
private static final Runtime runtime = Runtime.getRuntime();
private static final int OBJECT_COUNT = 100000;
/**
* Return the size of an object instantiated using the instantiator
*
* #param instantiator
* #return byte size of the instantiate object
*/
static public int execute(Instantiator instantiator) {
runGarbageCollection();
usedMemory();
Object[] objects = new Object[OBJECT_COUNT + 1];
long heapSize = 0;
for (int i = 0; i < OBJECT_COUNT + 1; ++i) {
Object object = instantiator.execute();
if (i > 0)
objects[i] = object;
else {
object = null;
runGarbageCollection();
heapSize = usedMemory();
}
}
runGarbageCollection();
long heap2 = usedMemory(); // Take an after heap snapshot:
final int size = Math.round(((float) (heap2 - heapSize)) / OBJECT_COUNT);
for (int i = 1; i < OBJECT_COUNT + 1; ++i)
objects[i] = null;
objects = null;
return size;
}
private static void runGarbageCollection() {
for (int r = 0; r < 4; ++r){
long usedMem1 = usedMemory();
long usedMem2 = Long.MAX_VALUE;
for (int i = 0; (usedMem1 < usedMem2) && (i < 500); ++i) {
runtime.runFinalization();
runtime.gc();
Thread.yield();
usedMem2 = usedMem1;
usedMem1 = usedMemory();
}
}
}
private static long usedMemory() {
return runtime.totalMemory() - runtime.freeMemory();
}
}
Implement the interface
public interface Instantiator { Object execute(); }
With the code you want to test
public void sizeOfInteger(){
int size = SizeOfUtil.execute(new Instantiator(){
#Override public Object execute() {
return new Integer (3);
}
});
System.out.println(Integer.class.getSimpleName() + " size = " + size + " bytes");
}
source : Java Tutorial Java Size of objects
I think this must help:
visualvm
it comes with jdk, and have many thing that help to control memory usage
you can get a very close value by comparing the free memory of the JVM before and after the loading of your program. The difference is very close to the memory usage of your program. To get the JVM free memory use
Runtime.getRuntime().freeMemory()
To get the memory usage do this:
public static void main (String args[]){
long initial = Runtime.getRuntime().freeMemory(); //this must be the first line of code executed
//run your program ... load gui etc
long memoryUsage = Runtime.getRuntime().freeMemory() - initial ;
}

Categories