A Java Virtual Machine is a runtime environment required for execution of a Java application.
Every Java application runs inside a runtime instance of some concrete implementation of abstract specifications of JVM.
It is JVM which is crux of 'platform independent' nature of the language.
When JVM executes a Java application, a runtime instance of JVM is born. This runtime instance invoke main() method of Java application. The main() method of an application serves as the starting point for that application's initial thread.
The initial thread can in turn fire off other threads. This thread has a program counter (PC) and Java stack. Whenever main() method is invoked, a stack frame is pushed onto the stack, this then becomes the active tack frame.
The program counter in the new Java stack frame will point to the beginning of the method. If there are more method invocations within main() method then this process of pushing new stack frame onto the stack for each method call is repeated as and when they are invoked.
When a method returns, the active frame is popped from the stack and the one below becomes the active stack frame. The PC is set to the instruction after the method call and the method continues.
There is only one heap corresponding to an instance of JVM and all objects created are stored here. This heap is shared by all threads created in an application. Inside the Java virtual machine, threads come in two flavors: daemon and non- daemon.
A daemon thread is ordinarily a thread used by the virtual machine itself, such as a thread that performs garbage collection. The application, however, can mark any threads it creates as daemon threads.
The initial thread of an application--the one that begins at main()--is a non- daemon thread. A Java application continues to execute (the virtual machine instance continues to live) as long as any non-daemon threads are still running.
When all non-daemon threads of a Java application terminate, the virtual machine instance will exit. If permitted by the security manager, the application can also cause its own demise by invoking the exit() method of class Runtime or System. When main() returns, it terminates the application's only non-daemon thread, which causes the virtual machine instance to exit.
Each time a Java Application is executed then an instance of JVM, responsible for its running, is created. A JVM instance is described in terms of subsystems, memory areas, data types, and instructions.
The block diagram given below depicts a view of Internal Architecture of JVM:
A Magic Number of a class file is a unique identifier for tools to quickly differentiate class files from non-class files.
The first four bytes of each Java class file has the magic value as 0xCAFEBABE.
And the answer to why this number, I do not actually know but there may be very few sensible and acceptable options possible constructed from letters A-F which can surely not be 'CAFEFACE' or 'FADECAFE'...
The heap is the part of memory of JVM where all objects reside.
The stack is consisted of stack frames.
When a thread invokes a method, the JVM pushes a new frame onto that thread's Java stack. Each stack frame is consisted of operand stack and the local variable array.
All arguments, local variables, intermediate computations and return values if any are kept in these stack corresponding to the method invoked. The stack frame on the top of the stack is called the active stack frame, which is the current place of execution. When the method completes, the virtual machine pops and discards the frame for that method.
JVM associates a lock with an object or a class to achieve multithreading. A lock is like a token or privilege that only one thread can "possess" at any one time. When a thread wants to lock a particular object or class, it asks the JVM.
JVM responds to thread with a lock maybe very soon, maybe later, or never. When the thread no longer needs the lock, it returns it to the JVM. If another thread has requested the same lock, the JVM passes the lock to that thread. If a thread has a lock, no other thread can access the locked data until the thread that owns the lock releases it.
The JVM uses locks in conjunction with monitors. A monitor is basically a guardian in that it watches over a sequence of code, making sure only one thread at a time executes the code. Each monitor is associated with an object reference. It is the responsibility of monitor to watch an arriving thread must obtain a lock on the referenced object. When the thread leaves the block, it releases the lock on the associated object.
A single thread is allowed to lock the same object multiple times. JVM maintains a count of the number of times the object has been locked. An unlocked object has a count of zero. When a thread acquires the lock for the first time, the count is incremented to one. Each time the thread acquires a lock on the same object, a count is incremented. Each time the thread releases the lock, the count is decremented. When the count reaches zero, the lock is released and made available to other threads. In Java language terminology, the coordination of multiple threads that must access shared data is called synchronization.
The language provides two built-in ways to synchronize access to data: with synchronized statements or synchronized methods. The JVM does not use any special opcodes to invoke or return from synchronized methods.
When the JVM resolves the symbolic reference to a method, it determines whether the method is synchronized. If it is, the JVM acquires a lock before invoking the method. For an instance method, the JVM acquires the lock associated with the object upon which the method is being invoked. For a class method, it acquires the lock associated with the class to which the method belongs.
After a synchronized method completes, whether it completes by returning or by throwing an exception, the lock is released. Two opcodes, monitorenter and monitorexit are used by JVM for accomplishing this task. When monitorenter is encountered by the Java virtual machine, it acquires the lock for the object referred to by objectref on the stack. If the thread already owns the lock for that object, a count is incremented. Each time monitorexit is executed for the thread on the object, the count is decremented. When the count reaches zero, the monitor is released.
The Class loader is a subsystem of a JVM which is responsible, predominantly for loading classes and interfaces in the system. Apart from this, a class loader is responsible for the following activities:-
Verification of imported types (classes and interfaces)-Allocating memory for class variables and initializing them to default values. Static fields for a class are created and these are set to standard default values but they are not explicitly initialized.
The method tables are constructed for the class.-Resolving symbolic references from type to direct references The class loaders can be of two types: a bootstrap or primordial class loader and user defined class loaderEach JVM has a bootstrap class loader which loads trusted classes, including classes from Java API.
JVM specs do not tell how to locate these classes and is left to implementation designers. A Java application with user defined class loader objects can customize class loading. These load untrustworthy classes and not an intrinsic part of JVM. They are written in Java, converted to class files and loaded into the JVM and installed like any other objects.
Try using - Xaprof to get a profile of the allocations (objects and sizes) of your application.
Also try - agentlib:hprof=heap=all (or other option, try - agentlib:hprof=help for a list).
Whenever a reference to an object on heap lies dangling or no longer in use then it becomes eligible for being garbage collected by JVM.
JVM specifications do not force any specific kind of garbage collection algorithm though there are several algorithms like reference counting, tracing, compacting, copying, generational etc. in place.
It is very important that garbage collection should be efficient and non-interfering in execution of Java programs.
There is a trade off between ease of implementation versus better performance while implementing garbage collection feature for a JVM.
The answer is No! Pooling objects will cause them to live longer than necessary. The garbage collection methods will be much more efficient if you let it do the memory management. The strong advice is taking out object pools. Don’t call System.gc(), HotSpot will make the determination of when it’s appropriate and will generally do a much better job.
You may be running into a problem with the default stack size for threads. In Java SE 6, the default on Sparc is 512k in the 32-bit VM, and 1024k in the 64-bit VM.
On x86 Solaris/Linux it is 320k in the 32-bit VM and 1024k in the 64-bit VM. On Windows, the default thread stack size is read from the binary (java.exe). As of Java SE 6, this value is 320k in the 32-bit VM and 1024k in the 64-bit VM. You can reduce your stack size by running with the -Xss option.
For example: java -server -Xss64kNote that on some versions of Windows, the OS may round up thread stack sizes using very coarse granularity. If the requested size is less than the default size by 1K or more, the stack size is rounded up to the default; otherwise, the stack size is rounded up to a multiple of 1 MB.64k is the least amount of stack space allowed per thread.
The Java HotSpot VM cannot expand its heap size if memory is completely allocated and no swap space is available. This can occur, for example, when several applications are running simultaneously.
When this happens, the VM will exit after printing a message similar to the following.
Exception java.lang.OutOfMemoryError: requested bytesIf you see this symptom, consider increasing the available swap space by allocating more of your disk for virtual memory and/or by limiting the number of applications you run simultaneously.
You may also be able to avoid this problem by setting the command-line flags -Xmx and -Xms to the same value to prevent the VM from trying to expand the heap.
Note that simply increasing the value of -Xmx will not help when no swap space is available.
GUI intensive Java application mostly run underlying OS specific native libraries which is time and more CPU cycles consuming.
The overall performance of a Java application depends on four factors :
The design of the application
The speed at which the virtual machine executes the Java bytecodes
The speed at which the libraries that perform basic functional tasks execute (in native code)
The speed of the underlying hardware and operating system
The virtual machine is responsible for byte code execution, storage allocation, thread synchronization, etc.
Running with the virtual machine are native code libraries that handle input and output through the operating system, especially graphics operations through the window system.
Programs that spend significant portions of their time in those native code libraries will not see their performance on HotSpot improved as much as programs that spend most of their time executing byte codes.
Programs that spend significant portions of their time in those native code libraries will not see their performance on HotSpot improved as much as programs that spend most of their time executing byte codes.
A 64-bit version of Java has been available to Solaris SPARC users since the 1.4.0 release of J2SE.
A 64-bit capable J2SE is an implementation of the Java SDK (and the JRE along with it) that runs in the 64-bit environment of a 64-bit OS on a 64-bit processor. The primary advantage of running Java in a 64-bit environment is the larger address space. This allows for a much larger Java heap size and an increased maximum number of Java Threads, which is needed for certain kinds of large or long-running applications.
The primary complication in doing such a port is that the sizes of some native data types are changed. Not surprisingly the size of pointers is increased to 64 bits.
On Solaris and most Unix platforms, the size of the C language long is also increased to 64 bits.
Any native code in the 32-bit SDK implementation that relied on the old sizes of these data types is likely to require updating. Within the parts of the SDK written in Java things are simpler, since Java specifies the sizes of its primitive data types precisely. However even some Java code needs updating, such as when a Java int is used to store a value passed to it from a part of the implementation written in C.
A Java Runtime Environment (JRE) is a prerequisite for running Java applications on any computer.
A JRE contains a Java Virtual Machine (JVM), all standard, core java classes and runtime libraries. It does not contain any development tools such as compiler, debugger, etc.
JDK (Java Development Kit) is a whole package required to Java Development which essentially contains JRE+JVM, and tools required to compile and debug, execute Java applications.