The agenda will cover all aspects of the new J2SE 5 product - First we'll examine some of the motivations for the design of J2SE 5 and the main themes used - We will look at the big changes to the Java language, something that has changed little until Tiger. We'll go into the technical details of generics and metadata to help you understand how to get the most from these features - J2SE 5 has a lot of new library features. We won't go over all of these because of time constraints, but will focus mostly on the new concurrency utilities that greatly simplify writing multi-threaded applications and gove the programmer a wealth of choice on how to do this. - We'll look at the changes that have been made at the virtual machine level, seeing how the VM is easier to configure to achieve optimal performance and the new monitoring and manageability features for enterprise deployments. - At the end we'll sum up and provide you with links to places where you can get more detailed information.
The main design themes for J2SE 5 were: quality, stability and compatability: Java is used extensively in enterprise, mission critical applications. Customers want a product that is rock solid and will run without problems for as long as they need. Since there are a number of fundamental changes in this release we've been very careful to ensure that moving to J2SE 5 is as painless as possible for existing applications. We don't want people to have to do lots of porting to get their applications running. Java is truly write once, run anywhere and it's important that Java can fully support every typre of environment from desktop to data centre. We've put a lot of effort into ensuring that the desktop is not neglected and applications can be written that fully fit into the native windowing environment and have performance almost as good (if not better) than native applications. Since Java is so popular for server applications Sun have also put more effort into ensuring that the algorithms used within the VM for things like memory management and threads will scale to the biggest machines that money can buy. People are always asking for more features in Java and Sun have tried to address as many of those as they can and which make sense. Since compatability and portability are such basic foundations of Java Sun have been very careful to introduce these new features in an evolutionary rather than revolutionary way. Finally, probably the biggest single motivation for many of the changes in J2SE 5 was the idea of making Java easier to develop. Java is a third generation language and requires good programming skills to use effectively. As you'll see many of the new features are designed to make programming easier and the end result less prone to errors
Here we see a brief history of the Java language, showing the changes that have been made over time As we all know Java was launched in 1995 and has become a very popular language for developement of all types of applications. As with any new language, developers pretty soon found that there were some things that needed to be fine tuned. Java was lucky in that the 1.1 release only introduced relatively minor changes: the ability to create inner classes and the new event model that would scale much better than the old version. In JDK 1.2 and 1.3 there were no changes to the language; many new classes were added and a lot of work was done on the performance of the VM but the language remained the same. In JDK1.4 a minor change was made by introducing assertions. With the launch of JDK 5.0 we're seeing the biggest changes to the Java language since its launch, and we don't just mean in terms of the version number.
Unfortunately, there aren't many benchmarks that span back to the beginning of Java. This one, (named Volano) probably isn't the best as a general purpose benchmark either. But the improvement is dramatic: 7 times better performance since 1.2.2. But this doesn't tell the whole story: The Java platform has gotten better, and this benchmark doesn't take advantage of that!
Now let's spend sometime talking abut Java SE 5 Features
Let's start then by looking at the new features included at the language level in J2SE 5
This slide lists the seven new key features in J2SE 5. We'll describe each of these features. For generics and metadata we're going to go into a lot of detail to help you understand all of the issues around using these features correctly and effectively.
Generics is a feature that has been requested many times in Java. Sun have had to be very careful about how to introduce this feature in a way that minimises the impact of compatability. As we'll see generics is a rather bad name for this feature since what we're really doing is allowing classes to be made type specific rather than type generic (which is really the situation that we have had all along in Java).
OK, Lets take a look at Pre Java SE 5.0 code here. Can anyone tell if there is a problem here. Is it a compile time problem or a runtime problem? What problem? => Runtime exception
This piece of code illustrates the problem in Java that generics addresses. A programmer has created a method called expurgate that takes a collection and removes all strings who's length is four characters from it. The method takes a Collection as a parameter and then iterates through all of the elements looking for four character strings. The Collection can hold objects of any type (meaning strictly speaking that it is generic - see the naming problem?) When we extract an element from the collection we need to do an explicit cast to tell the compiler the type of the object we're getting. This is dangerous, as we can see. A different programmer creates a Vector and rather than adding a String, adds an Integer object. This all compiles fine, as Integer is still a sub-class of Object which is the type of the argument for the add method. However, at run time when the code calls expurgate the VM will throw a ClassCastException since the Integer object is not a String or sub-type of String. This is bad because it is a runtime exception. This code could be running in a telco billing application where runtime exceptions mean potentially large loss of revenue. What we need is a way to tell the compiler, not the VM what type the collection is so that the compiler can check all uses and report an error when the code is compiled, not when it's running. This is what generics do.
To restate the problem, the issue is that the compiler cannot check the types of the objects being stored in a collection because it has no way of knowing the type of the object, other than that it will be an Object or sub-type, which all classes by default are. All assignments when using the collections must do an explicit cast and can generate a runtime ClassCastException. The solution is for the programmer to give an explicit typing to a collection (or other class where this is required). By doing that the compiler can now check that all items added to a typed collection are correct. If we try to add an Integer to a String typed collection we will get a compiler error telling us that it is an inappropriate type. We can make the changes and prevent a runtime exception. The implementation of this feature is only in terms of compilation. The compiler is effectively adding the explicit cast for you and ensuring that only the correct type of obejcts are added. This is a key point from the perspective of compatability, which we'll discuss in more detail later. This is guaranteed to succeed from the point of view of eliminating ClassCastExceptions caused by this type of error. The asterisk is there because this will only hold true if all the code of an application is recompiled using the J2SE 5 compiler, so that all references can be checked. Pre 5.0 class files will still run using the new JVM. If you mix old and new class files you could still get a ClassCastException since the compiler will have been unable to check that the type of object being added to a collection is correct.
In J2SE 5 all the collection classes (as well as many others) have been made generic. Here we look at the same example we had earlier that would have thrown a ClassCastException. Using generics, when we instantiate the Vector class we also include a type argument to tell the compiler that the Vector is only to be used to hold objects of type String (String is final so it's not possible to use a sub-class). If we then try and call the add method with an Integer object as a parameter the compiler will know this is wrong and will report an error. Lets declare another Vector, this time parameterised for Integer objects. If we return the comparison between the two Vectors what will the result be, true or false? The answer is true. The base type of the objects is still the same, they are both Vector and they both share the same class at runtime. The fact that one has been typed to hold Strings and one has been typed to hold Integers does not affect this comparison. This is an important fact to remember as we'll see later when we discuss type erasure.
Well of course you all know that I wouldn't ask if the answer was yes. The problem is that A collection with the type argument of Objects is NOT a supertype of all types of collection. If you try to call this method and pass in a collection with type argument String you will get a compiler error due to the type mismatch. How do we therefore solve this problem? Invariant sub-typing: Collection is not a super-class of Collection Remember earlier when we saw the comparison of the two references? The base types are the same, but parameters do not have inheritance in the same way as concrete classes.
The answer is using what is called a wildcard type argument. This is the question mark that we saw earlier in the syntax definition for type arguments. The wildcard type is equivalent to saying the type is unknown, so Collection<?> is the same as saying a Collection of unknown type. This is also something important to remember as we'll see shortly
There may be situations where we want to be able to define a type argument so that it is restricted to a particular class or sub-type of that class. Unlike normal classes, parameterised types do not have inheritance - we saw this earlier where a Collection of type String could not be treated as a sub-class of Collection of type Object. In this example we want to define a method that can draw a set of shapes taken from a List. Since we have a good OO design let's say we've defined an interface called Shape which has concrete implementations called Circle and Triangle. If we defined the drawAll() method to take a List of type Shape we'd get compiler errors trying to pass a List of Circle or Triangle objects.. We could use the wildcard type, but then we could pass any List to the method including ones that did not contain Shape objects. What we need to do is use a bounded wildcard, which is what we do here. We still use the wildcard type but we also specify that it extends Shape. This means that only a List typed to contain objects that are a sub-class of Shape can be passed as a parameter (remember that Shape is a sub-class of itself). Our example will compile and work as we want it to do.
Generics is a feature that has been requested many times in Java. Sun have had to be very careful about how to introduce this feature in a way that minimises the impact of compatability. As we'll see generics is a rather bad name for this feature since what we're really doing is allowing classes to be made type specific rather than type generic (which is really the situation that we have had all along in Java).
The first feature is what's called autoboxing and unboxing. Java is an object oriented language, but to purist like Smalltalk developers it's not truly object oriented as it still has primitive types to represent numbers and booleans. The reason for including these is that if everything is treated as an object you will never be able to get the application to perform as well as natively compiled C and C++ code. The problem with primitives is that in order to store them in a collection they have to be made into an object. Java provides the wrapper classes like Integer and Short for this purpose. To add a primitive to a collection is therefore complex as we must first instantiate a new wrapper class object and then add it to the collection. Similarly when getting the value out of the collection we first must retrieve the wrapper object and then call the appropriate method. Auto-boxing and unboxing solves this problem by letting the compiler do the hard work for us. As we can see in the example we can simply assign the value 22 to the Byte wrapper class object. The compiler will instantiate the object for us. We can do this because Java does not support explicit pointers. In C or C++ we wouldn't know whether this was assigning the memory address 22 to this object reference. Since in Java you cannot do that, the compiler knows it must be an auto-boxing conversion. Similarly when we want to get the value back we simply say int i = byteObj and the compiler fills in the method call for us. This will also work in method calls so we can simply pass the value 22 to the add method of ArrayList and the compiler will replace that with add(new Integer(22)).
The first feature is what's called autoboxing and unboxing. Java is an object oriented language, but to purist like Smalltalk developers it's not truly object oriented as it still has primitive types to represent numbers and booleans. The reason for including these is that if everything is treated as an object you will never be able to get the application to perform as well as natively compiled C and C++ code. The problem with primitives is that in order to store them in a collection they have to be made into an object. Java provides the wrapper classes like Integer and Short for this purpose. To add a primitive to a collection is therefore complex as we must first instantiate a new wrapper class object and then add it to the collection. Similarly when getting the value out of the collection we first must retrieve the wrapper object and then call the appropriate method. Auto-boxing and unboxing solves this problem by letting the compiler do the hard work for us. As we can see in the example we can simply assign the value 22 to the Byte wrapper class object. The compiler will instantiate the object for us. We can do this because Java does not support explicit pointers. In C or C++ we wouldn't know whether this was assigning the memory address 22 to this object reference. Since in Java you cannot do that, the compiler knows it must be an auto-boxing conversion. Similarly when we want to get the value back we simply say int i = byteObj and the compiler fills in the method call for us. This will also work in method calls so we can simply pass the value 22 to the add method of ArrayList and the compiler will replace that with add(new Integer(22)).
Generics is a feature that has been requested many times in Java. Sun have had to be very careful about how to introduce this feature in a way that minimises the impact of compatability. As we'll see generics is a rather bad name for this feature since what we're really doing is allowing classes to be made type specific rather than type generic (which is really the situation that we have had all along in Java).
The next new feature is the enhanced for loop, If we have a collection and want to iterate over all the elements we use a for loop and an Iterator object. This is potentally error prone as the for loop has three parts to it initialisation, test and increment. The iterator can be used in all three of these places; if cut and paste is used incorrectly or an identifier is mis-typed some very subtle and hard to find bugs can be introduced. The solution is to let the compiler do the hard work for you again. To do this, the for loop has had a new syntactical construct added. Rather than using the traditional three statements separated by semi-colons, we have a single statement of the form variable defintiion colon collection name. This tells the compiler that we want to iterate over the collection specified and will use the variable identifier within the loop to reference each item. This sytax will work either for a collection or for an array.
Here we see a good example of old and new style programming. We have a method that is designed to cancel a set of timer tasks. In the old code we would need to retrieve the Iterator from the collection passed as a parameter, which is the initialisation of the for loop. The test of the for loop uses the hasNext method of the iterator and then within the body of the foor loop we use the next method to retrieve the element. We also need to do an explicit cast so that the object retrieved from the collection can be assigned to the correct type. Obvioulsy, this is rather verbose and prone to errors when using the same Iterator three times. The new code makes this much simpler. We simply use the new for loop construct to define a variable, task that will be used in the body of the for loop, being of type TimerTask and iterating over the collection passed in, c. We then simply call the cancel method on task. We're also using generics in this example to tell the compiler the type of the collection.
Generics is a feature that has been requested many times in Java. Sun have had to be very careful about how to introduce this feature in a way that minimises the impact of compatability. As we'll see generics is a rather bad name for this feature since what we're really doing is allowing classes to be made type specific rather than type generic (which is really the situation that we have had all along in Java).
Often there are times when a programmer wants to have a variable that can only hold one of a fixed set of values. Typically we do this by assigning a number to each value and then storing it as an integer, using final static defintitions to assign the appropriate values. This is not a reliable mechanism which is why J2SE has introduced type safe enumerations. An enumeration is a new kind of class definition which uses the keyword enum to differentiate it from a normal class. Even though an enumeration is like a class in many way, it is not the same. You cannot instantiate an enumeration; this is effectively done once by the VM when the system. The definition of an enumeration contains a set of public, self-typed members which are used as the constants of that type. These values can be used in a switch statement with the case keyword. Previously all values used with a case statement had to be integers.(Sang: so you can do swtich statement with a typed object?) The compiler will handle the use of the enumeration correctly. With the introduction of enumerations Java now has a new reserved word, enum. This is a consideration from a migration perspective since any code using enum as an identifier will need to be changed to compile under J2SE 5.
First example of the use of an enumeration. This code uses two simple enumerations to represent the cards in a pack. One enumeration is used for the suits and one is used for the values. The definition is very simple, we just define the enumeration as having the constants we want separated by commas. We can then refer to these constants in our program, for example in a switch statement. For this example we create an ArrayList to hold the pack of cards. We use a type argument to tell the compiler that we want this List to only hold objects of type Card (which is defined elsewhere). We can then use the new for loop construct to iterate over the enumerations, populating the list. Notice the use of the values() method to return a collection of the constants of the enumeration. Finally we shuiffle the deck using the shuffle() utility method of the Collections class. Using a combination of features from J2SE 5 we can see that code can become much more concise, easy to read and easy to maintain. Imagine writing this code in pre J2SE 5 Java; it would take 3 or 4 times as much code
.
Generics is a feature that has been requested many times in Java. Sun have had to be very careful about how to introduce this feature in a way that minimises the impact of compatability. As we'll see generics is a rather bad name for this feature since what we're really doing is allowing classes to be made type specific rather than type generic (which is really the situation that we have had all along in Java).
Java has always supported import declarations that allow types from other packages to be referred to using their simple names, i.e. without using a fully qualified class name. However, the static members of classes have not been treated in the same way; for example, whenever you refer to a static member of the Color class it's necessary to explicitly state Color.yellow or Color.blue. In J2SE 5 the import mechanism has been extended to include static members. To use this the static keyword is added to the normal import to indicate that static members can be accessed without specifying the class name. As with normal imports the wildcard * can be used to indicate all members of the package. The import applies to all static members of the class including methods and enums.
Lets look at some of the changes and new features in the Java Virtual Machine
Now that Java supports varargs, the much loved (or maligned) printf library call has been added that will be familiar to C and C++ programmers. If you have not used these languages before you will want to have a look at the manual page to see exactly how to use this. printf takes a string as an argument that will be displayed on the standard output. This string can contain arguments identified by the % character that will be replaced by the values of the other arguments when the string is displayed. The order of the arguments in the string must match the order of the arguments in the method call. The % character is followed by characters to indicate what type the argument is and how it should be formatted for display. For example, %s indicates a free format string; %2.2F indicates a floating point number that should have two digits before and after the decimal point. Since Java is cross platform you can use %n to specify a newline that will be correct for the current OS. and can still be used, but these will only output carriage return and linefeed. The scanf function that is the inverse of printf has not been included. The Scanner class provides a simple text scanner which can parse primitive types and strings using regular expressions. A Scanner breaks its input into tokens using a delimiter pattern, which by default matches whitespace. The resulting tokens may then be converted into values of different types using the various next methods. In the example shown, the code allows a user to read a number from System.in
Lets look at some of the changes and new features in the Java Virtual Machine
Metadata allows programmer to specify annotations for parts of a program. An annotation is a modifier consisting of an annotation type and zero or more element-value pairs, each of which associates a value with a different element of the annotation type. The purpose of an annotation is simply to associate information with the annotated program element. Annotations can be used by tools such as compilers, IDEs and runtime tools to produce derived files, whether that is something like a new source file, a deployment descriptor or some other file. Java already has some annotations, but these are not organised in a formal way, which is what metadata does. For example, the Serializeable interface contains no methods. If you create a class that implements Serializeable, what does it do? The answer is that implementing the interface is used as a marker (or annotation) to tell the compiler that this class can be serialised. The compiler will then add apropriate code to perform serialisation when required. Xdoclets is another example of a form of annotation that can be used to derive deployment descriptors, etc from infromation contained in the source file. Annotations may be used as modifiers in any declaration, whether package, class, interface, field, method, parameter, constructor, or local variable. Annotations may also be used on enum constants. Such annotations are placed immediately before the enum constant they annotate.
Lets look at some of the changes and new features in the Java Virtual Machine
New I/O APIs The new I/O (NIO) APIs introduced in version 1.4 provide new features and improved performance in the areas of buffer management, scalable network and file I/O, character-set support, and regular-expression matching. These new APIs are supported in both client and server compilers. With NIO, developers can now write ultra-scalable, high-performance server applications such as Web, application, file, and database servers. They can also write compute-intensive scientific, technical, and graphics applications that require fast access to large quantities of data. I/O operations that previously required programming in C or C++ can now be performed using the Java language, but with the performance of a native C or C++ application. · The new network I/O package dramatically increases the number of simultaneous connections that a server can handle by removing the need to dedicate one thread to every open connection. · New file I/O supports read, write, copy, and transfer operations that are up to twice as fast as the current file I/O facilities. It also supports file locking, memory-mapped files, and multiple concurrent read/write operations.
Unfortunately, there aren't many benchmarks that span back to the beginning of Java. This one, (named Volano) probably isn't the best as a general purpose benchmark either. But the improvement is dramatic: 7 times better performance since 1.2.2. But this doesn't tell the whole story: The Java platform has gotten better, and this benchmark doesn't take advantage of that!
Unfortunately, there aren't many benchmarks that span back to the beginning of Java. This one, (named Volano) probably isn't the best as a general purpose benchmark either. But the improvement is dramatic: 7 times better performance since 1.2.2. But this doesn't tell the whole story: The Java platform has gotten better, and this benchmark doesn't take advantage of that!
Lets look at some of the changes and new features in the Java Virtual Machine
Previously Java has supported several very primitive mechanisms for co-ordinating a number of threads. The synchronized keyword can be applied to methods or code blocks and the Thread class supports the wait, notify, sleep and interrupt mothods. The problem with these primitive mechanisms is that they are just that: primitive. As we will see the new utilities will greatly enhance Java applications in terms of scalability, performance, readability and thread safety.
The idea behind the new concurrency utilities is to provide a much richer set of functions that programmers can use to create simple and powerful multi-threaded applications, in the same way that the Collections classes provided a much richer set of data structure based APIs. By providing much finer granularity of locking and different approaches such as multiple read-single write locks a secondary aim is t o enable the performance of a multi-threaded Java application to match or exceed that of native C applications in the high-end server environment. Previously Java has supported several very primitive mechanisms for co-ordinating a number of threads. The synchronized keyword can be applied to methods or code blocks and the Thread class supports the wait, notify, sleep and interrupt mothods. The problem with these primitive mechanisms is that they are just that: primitive. As we will see the new utilities will greatly enhance Java applications in terms of scalability, performance, readability and thread safety.
Here is a list of things we'll talk about in this session. This is not an exhaustive list of all the new features, but given the limited time we have this should give you a good grasp of the main areas of functionality. One of the main changes in using the new concurrency utilities is the concept of moving away from interacting directly with a thread object. As we'll see the new and preferred way is through an interface called ExecutorService. There are several factory methods available to easily provide the programmer with standardised mechanisms for the Executor such as thread pools, single thread and priority threads. # Task Scheduling Framework - The Executor framework is a framework for standardizing invocation, scheduling, execution, and control of asynchronous tasks according to a set of execution policies. Implementations are provided that allow tasks to be executed within the submitting thread, in a single background thread (as with events in Swing), in a newly created thread, or in a thread pool, and developers can create of Executor supporting arbitrary execution policies. The built-in implementations offer configurable policies such as queue length limits and saturation policy which can improve the stability of applications by preventing runaway resource consumption. # Concurrent Collections - Several new Collections classes have been added, including the new Queue and BlockingQueue interfaces, and high-performance, concurrent implementations of Map, List, and Queue. Until now it has required relatively complex coding to allow a child thread to return a result to the its parent. This is even more complex when it is necessary to synchonize the threads so that the parent can only continue when the child has completed generatijng the result. This becomes very simple now through the use of Callable and Future. Semaphores are a well understood mechanism that are now supported on Java. BlockingQueues allow simple data structures to be used by multiple threads in a concurrent way such that the programmer is not responsible for ensuring safe concurrent access. Lastly the idea of an Atomic variable that can safely be accessed and modified is also iincluded in the concurrency utilities.
Here is a list of things we'll talk about in this session. This is not an exhaustive list of all the new features, but given the limited time we have this should give you a good grasp of the main areas of functionality. One of the main changes in using the new concurrency utilities is the concept of moving away from interacting directly with a thread object. As we'll see the new and preferred way is through an interface called ExecutorService. There are several factory methods available to easily provide the programmer with standardised mechanisms for the Executor such as thread pools, single thread and priority threads. # Task Scheduling Framework - The Executor framework is a framework for standardizing invocation, scheduling, execution, and control of asynchronous tasks according to a set of execution policies. Implementations are provided that allow tasks to be executed within the submitting thread, in a single background thread (as with events in Swing), in a newly created thread, or in a thread pool, and developers can create of Executor supporting arbitrary execution policies. The built-in implementations offer configurable policies such as queue length limits and saturation policy which can improve the stability of applications by preventing runaway resource consumption. # Concurrent Collections - Several new Collections classes have been added, including the new Queue and BlockingQueue interfaces, and high-performance, concurrent implementations of Map, List, and Queue. Until now it has required relatively complex coding to allow a child thread to return a result to the its parent. This is even more complex when it is necessary to synchonize the threads so that the parent can only continue when the child has completed generatijng the result. This becomes very simple now through the use of Callable and Future. Semaphores are a well understood mechanism that are now supported on Java. BlockingQueues allow simple data structures to be used by multiple threads in a concurrent way such that the programmer is not responsible for ensuring safe concurrent access. Lastly the idea of an Atomic variable that can safely be accessed and modified is also iincluded in the concurrency utilities.
As mentioned earlier programmers should now not interact directly with the Thread class. Before, we would create a class that implemented the Runnable interface. To start this in a new thread we would use a line like this: create a new thread with using the Runnable class and call the start method that would in turn call the run method in our class. This is still quite correct, but the idea is to replace this with an abstracted interface, Executor. Instead of calling start we call execute. Since this is abstracted away from the Thread class it becomes a simple task to change the way we handle the threading should we wish to do so at a later date. For example, we could start with a piece of code that creates a single thread to execute our new code. As requirements and processing power change we find that we need to run a number of threads for our class. We can simply change the factory method we use to create a thread pool and we are then able to use the same class in a number of threads rather than just one.
Here is an example of code that uses the new Executor, Executors and ExecutorService classes. The example is a standard web service class that needs to handle a number of incoming connections simultaneously through a number of separate threads. The number of threads needs to be bounded to prevent the system from running out of resources when the load becomes too high. Previously it would have been necessary to create your own thread pooling class that would create a set of threads and then manage all of the alloaction and deallocation of those threads with all of the required concurrent access controls. With the concurrency utilities this is all provided by default. In the main routine we initialise a new fixed thread pool with a size of 7. We use the newFixedThreadPool method of the Executors class. This will return a ExecutorService object. Since ExecutorService implements the Executor interface we can assign it to an Executor object reference. To handle an incoming connection we simply call execute on our pool object passing it a Runnable object (which in this case is defined through an inner class). The run method does whatever work we need the thread to do. Whenever connections come in they will be allocates a thread from the pool. When the run method completes the thread will automatically be returned to the pool. If a connection comes in and all threads are in use the main loop will block until a thread is freed.
The Lock interface provides more extensive locking operations than using a synchronized block. Because we are using methods of a class to explicitly perform the lock and unlock operations the programmer needs to be more careful in its use. With a synchronized block of code it is impossible to forget to unlock it. Once the code execution leaves that block whether normally or through some for of exception, the lock is released. Using the Lock class the programmer must typically use a try-finally construct and put the unlock call within the finally clause to ensure that the lock is always released. One advantage of the Lock interface over synchronized is the ability to not block if a lock is not available. The tryLock method will always return immediately, returning true if the lock was aquired or false if not. This method may also be called with a timeout parameter so the thread will only block for the specified time if the lock is not aquired. ReentrantLock provides a concrete implementation of the Lock interface. The thread that holds the lock can call the lock method multiple times without blocking. This is especially useful in recursive code that needs to protect access to a certain section of code.
ReentrantLock provides a concrete implementation of the Lock interface. The thread that holds the lock can call the lock method multiple times without blocking. This is especially useful in recursive code that needs to protect access to a certain section of code.
The ReadWriteLock permits multiple threads to have read access to a protected piece of code, but only one thread may access the code in write mode. Effectively the ReadWriteLock consists of two locks that are implemented as inner classes. If a thread aquires the read lock other threads may also aquire the read lock. If a read lock is held no thread may aquire the write lock until all read locks have been released. If a thread holds the write lock, no thread may aquire either the write lock or a read lock. ReadWriteLock is an interface. RentrantReadWriteLock is a concrete implementation of this interface.
The BlockingQueue interface is a Queue that additionally supports operations that wait for the queue to become non-empty when retrieving an element, and wait for space to become available in the queue when storing an element. A BlockingQueue does not accept null elements. ArrayBlockingQueue is a concrete implementation of the BlockingQueue interface. An ArrayBlockingQueue is a bounded blocking queue backed by an array. This queue orders elements FIFO (first-in-first-out). The head of the queue is that element that has been on the queue the longest time. The tail of the queue is that element that has been on the queue the shortest time. New elements are inserted at the tail of the queue, and the queue retrieval operations obtain elements at the head of the queue. The Blocking queue provides methods to insert and remove objects from the queue (it is generic so can be typed). put() adds the specified element to the tail of this queue, waiting if necessary for space to become available. offer() inserts the specified element at the tail of this queue if possible, returning immediately if this queue is full. peek() retrieves, but does not remove, the head of this queue, returning null if this queue is empty. take() retrieves and removes the head of this queue, waiting if no elements are present on this queue. poll() retrieves and removes the head of this queue, null if this queue is empty. poll can also be specified with a time out so that the call will wait if necessary up to the specified wait time if no elements are present on this queue.
Here is an example of the use of a BlockingQueue. The class implements a logger that will be used by a number of threads to record information. The constructor takes a BlockingQueue (with type argument String). In the run method messages are retrieved from the queue using the take method. When the queue is empty the logging thread will block until messages become available. Once a message is retrieved it can be logged in whichever way is required.
Having defined our logging thread here is a class that uses the logger. A new ArrayBlockingQueue is instantiated with a type argument of String and a size of 10 elements. This is passed to the logger constructor as required. We can now start a number of threads that use this logger to record messages. We use the put method on the messageQueue. If the queue is full the thread will block until the logger has removed messages. The BlockingQueue will handle contention in a thread safe way should multiple threads be waiting for space to become available in the queue.
Lets look at some of the changes and new features in the Java Virtual Machine
Why are we here? In C/C++ you do the memory management. You make the calls to malloc() and the calls to free(). Forget the calls to free() and you're leaking memory.. And, of course, you don't use the memory once it's been freed. And, you free memory exactly once.
In a nutshell, a GC ... Our garbage collectors are generational, meaning we divide the heap into two regions and don't have to always collect the entire heap. When we only collect one of the regions we call it a minor collection. Minor collections are typically much faster than major collections and often collect enough memory so as to delay the more expensive major collection.
Java does the memory management for you. The JVM finds the data that is still in use by the program. This data is referred to a reachable. Anything else is collected as garbage. You never have the equivalent of a dangling pointer. If you have a reference to data, it's there, it has not been collected as garbage. No free's obviously means no double frees. In principle you cannot have a true memory leak but there are things that you can do that are as bad in practice. Basically, you have a reference to data that is never going to be used again. This is more accurately described as unintential object retention but is often just called a memory leak. If your program has such a memory leak, you'll ...
So garbage collection is your friend. The source of some ugly bugs has removed. You spend more time on the interesting stuff. You don't have to think about memory management as much in your design. But there are some costs. Your going to have pauses in the application execution when a GC occurs. You don't know when a GC is going to occur and don't know how long it is going to take. Finalization depends on GC's. User's of you programs may want to choose among the different collectors to achieve a particular performance (e.g., better throughput or shorter pause times). Some tuning may be required.
The point here is to make it so that the garbage collection proces is not as disruptive to your application. So the garbage collector works at the same time as your application , short stop do a little work then go back, so that you dont see one long pause. Has some overhead that lowers throughput a little. The young generatons collections are short already so there is no need to put that extra overhead on the young generation We only do incremental of the old generation
Before going further let me tell you about memory management on a modern Java platform. Allocation is definitely not slow. It was slow in ... Garbage collection has gotten much, much faster than in the early days but a collection does still happen all at the same time so it's noticeable. We don't use reference counting. That's notable because reference counting does slow down the execution of the program. Because early performance was an issue, there's some lingering advice on how to get better performance. Much of that is out dated. And some of the bad advice actually leads to memory leaks.
Memory allocation is fast , really cheap You don't have to keep track of the remembered set, tracking pointers from old to young, does not have to be done for younger objects. Short lived objects can be reclaimed very fast
Okay, so we don't have true memory leak, right? But we can hold onto objects that are never going to be used again. You can find plenty of examples of such objects ... In the best case such objects cause more work for the garbage collector. In the worst case you can get an out-of-memory exception because of them. In the next few slides we'll look at three examples of these types of memory leaks.
In this example an object that stays around longer than it is needed. “ byteArray” is part of “LeakyChecksum” so will live as long as the LeakyChecksum object live. It is. however only. needed during the invocation of geFileChecksum. Now maybe this is ok, but realize that byteArray is going to be as large as the largest file ever read, the garbage collector has to look at it at each collection, and the space would be used more profitably for the allocation of other objects.
This third example of a memory leak is less easy to workaround. You have an object and you want to associate some information with that object but you cannot put the information in the object itself. In this case you have a socket and want to associate a user id with the socket. A natural solution is to create a map between the socket and the user id as here in the SocketManager. Here the example uses a HashMap w
Here's the example with a fix using the WeakHasMap. WeakHashMap give you the direct connection between the key and the metadata that you need here. Don't replace all your HashMaps with WeakHashMaps. Reference processing does cost during GC and it would be a waste to always use it.
Have a explicit reason if you are going to null a reference. Mostly it doesn't help. Occasionally it's exactly the wrong thing to do. A System.gc() will trigger an full collections. In tuning the GC we often try hard to minimize full collections. Understand why you are doing System.gc(). Allocation is fast so just use it. Object pooling has costs in terms of filling up the heap so, again, understand what you are doing.
You are not guaranteed that a finalizer will ever run so, if you use them, you need design for that contigency. Regarding finalization we mostly hear from people who are trying to manage a scarce native resource which is probably the wrong thing to do. Try to use finally block first. That's the simplest and most deterministic.
Lets look at some of the changes and new features in the Java Virtual Machine
The big goal of “smart tuning” sometimes referred to as ergonomics, was good out-of-the-box performance for server applications. From the early days the VM has been tuned to run well with desktop applications because the overwelling majority of executions were for desktop applications. That hurts when customers run benchmarks for large server applications because that is often done without tuning the VM. In tiger we look at the machine we're running on and try to make some smarter choices. We've also added a simplified way of tuning garbage collection.
This slide shows the effects of tuning on 4 benchmarks. This is without “Smart tuning”. Bigger is better. The 1.4.2 untuned VM is in blue and the hand tuned tiger VM is in red. Tuning can make a big difference. Business logic – specjbb2000 Bytecodes – specjvm98 i/o – jetstream Scientific – scimark2
This is tiger tuned versus out-of-the-box performance on the same benchmarks. The blue is the out-of-the-box performance for tiger and the red again is the hand tuned tiger VM. Smart tuning has made tiger out-of-the-box performance is much closer to the tuned performance.
Lets look at some of the changes and new features in the Java Virtual Machine
Figure above shows the architecture of J2SE 5.0 monitoring and management support. In the 5.0 release, the Java virtual machine (JVM) is highly instrumented for monitoring and management. The platform instrumentation provides information on performance, resource consumption, and the JVM and logging settings of applications running on the Java platform. JMX provides a standard way to instrument the Java runtime environment and applications, and the JMX Remote API allows that instrumentation to be accessed remotely. The instrumentation is accessible through the JMX managed bean (MBean) interfaces, which are registered in the platform MBean server. Applications can also create their own MBeans and register them in the platform MBean server, which can serve as a single point for remote access. A JMX-compliant client, such as JConsole, can connect to the platform MBean server and manage the application (as well as the Java platform) using JMX technology.
java.lang.management provides the management interface for monitoring and management of the Java virtual machine as well as the operating system on which the Java virtual machine is running. It allows both local and remote monitoring and management of the running Java virtual machine. A platform MXBean is a managed bean that defines the management interface for one component for the platform and is specified in the ManagementFactory class. MXBeans are built into the JVM and can be accessed via, for example: RuntimeMXBean mxbean = ManagementFactory.getRuntimeMXBean(); The java.lang.management package defines the management interface of the following components: Management Interface Description ClassLoadingMXBean Class loading system of the Java virtual machine. CompilationMXBean Compilation system of the Java virtual machine. MemoryMXBean Memory system of the Java virtual machine. ThreadMXBean Threads system of the Java virtual machine. RuntimeMXBean Runtime system of the Java virtual machine. OperatingSystemMXBean OS on which the Java virtual machine is running. GarbageCollectorMXBean Garbage collector in the Java virtual machine. MemoryManagerMXBean Memory manager in the Java virtual machine. MemoryPoolMXBean Memory pool in the Java virtual machine.
Lets look at some of the changes and new features in the Java Virtual Machine
Application to manage car Loans for Toyota (>10 million loans)
Pharmaceutical Intranet ( Roche Switzerland)
Telecom Network Mgmt ( Digital France)
X.400 Email Server ( IBM Germany)
Agenda
Language Changes
Generics & Metadata
Library API Changes
Concurrency utilities
Garbage Collection
Virtual Machine
Monitoring & Management
Next Release: Mustang
J2SE 5.0 Design Themes
Focus on quality , stability, compatibility
Big emphasis on scalability
exploit big heaps, big I/O, big everything
Ease of development
Faster, cheaper, more reliable
Java Language Changes
JDK 1.0
Initial language, very popular
JDK1.1
Inner classes, new event model
JDK 1.2, 1.3
No changes at language level
JDK 1.4
Assertions (minor change)
JDK 5.0
Biggest changes to language since release 1.0
2004
2006
J2SE 5
“ Tiger”
Java SE 6
“ Mustang”
2007
Java SE 7
“ Dolphin”
Java SE road map
2008
2003
2002
2005
J2SE 1.4
EOL
J2SE 1.3.1
5.0 Java SE 5 2004/9
1. 6 .0 Java SE 6 2006/12
1. 7 .0 Java SE 7 2008
Tiger Is Everywhere Tiger 1.4.x Completed downloads per month
Over 262,295,496 downloads served! Tiger Is Everywhere
Pre-installed on > 60 % of new PCs Tiger Is Everywhere
≥ 99.999 % availability Previous best: 99.98% for 1.4.2 Tiger Is Stable
Tiger Is Fast Server Benchmark: SPECjbb2000
Tiger [Java SE 5] Features Overview
JDK 6 Features Overview
Java SE 1.5 Language Changes
Seven Major New Features
Generics
Autoboxing/Unboxing
Enhanced for loop (“foreach”)
Type-safe enumerations
Varargs
Static import
Metadata
Generics
Is there a problem in here? Vector v = new Vector(); v.add(new Integer (4)); OtherClass.expurgate(v); ... static void expurgate(Collection c) { for (Iterator it = c.iterator(); it.hasNext();) if ((( String )it.next()).length() == 4) it.remove(); }
The Problem (Pre-J2SE 5.0) Vector v = new Vector(); v.add(new Integer (4)); OtherClass.expurgate(v); ... static void expurgate(Collection c) { for (Iterator it = c.iterator(); it.hasNext();) /* ClassCastException */ if ((( String )it.next()).length() == 4) it.remove(); }
Generics
Problem: Collection element types
Compiler is unable to verify types
Assignment must use the cast operator
This can generate runtime errors ( ClassCastException )
Solution:
Tell the compiler what type the collection is
Let the compiler fill in the cast
Using Generic Classes
Instantiate a generic class to create type specific object
Example
Vector <String> x = new Vector <String> ();
x.add(new Integer(5)); // Compiler error!
Vector <Integer> y = new Vector <Integer> ();
Wildcards
Method to print contents of any Collection?
Wrong!
Passing a Collection of type String will give a compiler error
void printCollection(Collection <Object> c) { for (Object o : c) System.out.println( o ); }
Wildcards
Correct way:
? is the wildcard type
Collection <?> means Collection of unknown
void printCollection( Collection <?> c) { for (Object o : c) System.out.println(o); }
Bounded Wildcards
A wildcard can be specified with an upper bound
public void drawAll (List< ? extends Shape >s) { ... } List<Circle> c = getCircles(); drawAll(c) ; List<Triangle> t = getTriangles(); drawAll(t) ;
Autoboxing & Unboxing
Autoboxing/Unboxing of Primitive Types
Problem: (pre-J2SE 5.0) Conversion between primitive types and wrapper types (and vice-versa)
must manually convert a primitive type to a wrapper type before adding it to a collection
int i = 22;
List l = new LinkedList();
l.add( new Integer(i) );
Autoboxing/Unboxing of Primitive Types
Solution: Let the compiler do it
Integer intObj = 22 ; // Autoboxing conversion
int i = intObj; // Unboxing conversion
ArrayList< Integer > al = new ArrayList<Integer>();
al .add( i ); // Autoboxing conversion
Enhanced for Loop
Enhanced for Loop (foreach)
Problem: (pre-J2SE 5.0)
Iterating over collections is tricky
Often, iterator only used to get an element
Iterator is error prone (Can occur three times in a for loop)
Solution: Let the compiler do it
New for loop syntax for (variable : collection)
Works for Collections and arrays
Enhanced for Loop Example
Old code pre-J2SE 5.0
void cancelAll(Collection c) {
for ( Iterator i = c.iterator(); i.hasNext() ; ){
TimerTask task = (TimerTask) i.next();
task.cancel();
}
}
New Code
void cancelAll(Collection <TimerTask> c) {
for ( TimerTask task : c )
task.cancel();
}
Iterating over collections, tricky, error prone
New for loop syntax:
Let the compiler do it
Works for Collections and arrays
Type-safe Enumerations
Type-safe Enumerations
Problem: (pre-J2SE 5.0) to define an enumeration:
Defined a bunch of integer constants:
public static final int SEASON_WINTER = 0;
public static final int SEASON_SPRING = 1;
Issues of using Integer constants
Not type safe (any integer will pass),Brittleness (how do add value in-between?), Printed values uninformative (prints just int values)
Solution: New type of class declaration
enum type has public, self-typed members for each enum constant
enum Season { WINTER, SPRING, SUMMER, FALL }
Enumeration Example: public class Card { public enum Suit { spade, diamond, club, heart }; public enum Rank { ace, two, three, four, five, six, seven, eight, nine, ten, jack, queen, king }; private Card (Rank rank, Suit suit) { this.rank = rank; this.suit = suit; } } List< Card > deck = new ArrayList< Card >(); for ( Suit suit : Suit.values ()) for ( Rank rank : Rank.values ()) deck.add(new Card(rank, suit)); Think how much JDK1.4 code this would require!
Varargs
Before Varargs Example //example method that takes a variable number of parameters int sum( Integer[] numbers ) { for(int i: numbers) // do something } // Code fragment that calls the sum method sum( new Integer[] {12,13,20} ); http://www.javaworld.com/javaworld/jw-04-2004/jw-0426-tiger1.html
Problem: (in pre-J2SE 5.0) To have a method that takes a variable number of parameters
Can be done with an array, but caller has to create the array first
Varargs Example (Cont) //example method that takes a variable number of parameters int sum ( Integer... numbers ) { for(int i: numbers) // do something } // Code fragment that calls the sum method sum( 12,13,20 ); http://www.javaworld.com/javaworld/jw-04-2004/jw-0426-tiger1.html
Solution: Let the compiler do it for you:
String format (String fmt, Object... args);
Java now supports printf(...)
Varargs examples
APIs have been modified so that methods accept variable-length argument lists where appropriate
Class.getMethod
Method.invoke
Constructor.newInstance
Proxy.getProxyClass
MessageFormat.format
New APIs do this too
System.out. printf (“%d + %d = %d
”, a, b, a+b);
Static Imports
Static Imports
Problem: (pre-J2SE 5.0)
Having to fully qualify every static referenced from external classes
Solution: New import syntax
import static TypeName.Identifier;
import static Typename.*;
Also works for static methods and enums
e.g Math.sin(x) becomes sin(x)
Formatted I/O
Simple Formatted I/O
Printf is popular with C/C++ developers
Powerful, easy to use
Finally adding printf to J2SE 5.0 (using varargs)
out.printf(“%-12s is %2d long”, name, l);
out.printf(“value = %2.2F”, value);
Annotations
Annotations Metadata (JSR-175)
Provide standardised way of adding annotations to Java code
public @Remote void foo() {}
Annotations are used by tools that work with Java code:
Compiler
IDE
Runtime tools
Used to generate interfaces, deployment descriptors...
Annotations Example: JAX-RPC
Old Code
public interface PingIF implements java.rmi.Remote {
public void foo() throws java.rmi.RemoteException;
}
public class Ping implements PingIF {
public void foo() {...}
}
New Code
public class Ping {
public @Remote void foo() {}
}
Virtual Machine
Some Performance Enhancements in JDK 1.4
java.nio package
Non blocking I/O : improved performance in buffer management, scalable network and file i/o
Reflection
Many reflective operations rewritten for higher performance (20x)
Some J2SE 5.0 Performance Features
Concurrency libraries
Garbage collection improvements
“ Smart tuning”
Small systems performance optimizations
Client features
Class data sharing
Various JFC/Swing and Java 2D™ APIs improvements
X86 Optimizations
Quick Performance Fix
Always upgrade to the latest version of the JDK/JRE
Sun is always working to improve performance
Sun is always working to reduce the number of 'undocumented features'
Tiger is Fast Client Benchmark: SwingMark
Tiger Is Fast Server Benchmark: SPECjbb2000
x64 SPECjbb2005 Performance SPECjbb2005 Sun Fire X4100 (2 chip, 2 core, 2 threads) 32,018 SPECjbb2005 bops, 32,018 SPECjbb2005 bops/JVM, Sun Fire X4100 (2 chip, 2 core, 2 threads) 38,090 SPECjbb2005 bops, 19,045 SPECjbb2005 bops/JVM, submitted for review, IBM eServer p5 510 (2 chips, 2 cores, 4 thread) 36,039 bops, 36,039 bops/JVM, Dell SC1425 (2 chips, 2 cores, 4 thread) 24,208 SPECjbb2005 bops, 24,208 SPECjbb2005 bops/JVM, Dell PE 850 (1 chip, 2 cores, 2 thread) 31,138 SPECjbb2005 bops, 31,138 SPECjbb2005 bops/JVM. SPEC® and the benchmark name SPECjbb™ are trademarks of the Standard Performance Evaluation Corporation. Competitive benchmark results stated above reflect results published on www.spec.org as of May 11, 2005. For the latest SPECjbb2005 benchmark results, visit http://www.spec.org/osg/jbb2005. Sun Fire™ X4100 Server Wins
library for concurrency like Collections for data structures
Enhance scalability, performance, readability and thread safety of Java applications
Beat C performance in high-end server applications
Puzzle: “Ping Pong”
01 class PingPong {
02 public static synchronized void main (String[] a) {
03 Thread t = new Thread() {
04 public void run() {
05 pong();
06 }
07 };
08
09 t.run();
10 System.out.print("Ping");
11 }
12
13 static synchronized void pong() {
14 System.out.print("Pong");
15 }
16 }
What Does It Print?
(a) PingPong
(b) PongPing
(c) It varies
What Does It Print?
(a) PingPong
(b) PongPing
(c) It varies
Not a multithreaded program!
Example How to start a thread
public class HelloRunnable implements Runnable {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
(new Thread (new HelloRunnable())) .start();
}
}
Another Look
01 class PingPong {
02 public static synchronized void main(String[] a) {
03 Thread t = new Thread() {
04 public void run () {
05 pong();
06 }
07 };
08
09 t. run (); // Common typo!
10 System.out.print("Ping");
11 }
12
13 static synchronized void pong() {
14 System.out.print("Pong");
15 }
16 }
How Do You Fix It?
01 class PingPong {
02 public static synchronized void main(String[] a) {
03 Thread t = new Thread() {
04 public void run() {
05 pong();
06 }
07 };
08
09 t. start ();
10 System.out.print("Ping");
11 }
12
13 static synchronized void pong() {
14 System.out.print("Pong");
15 }
16 }
The Moral
Invoke Thread.start , not Thread.run
Common error
Can be very difficult to diagnose
Concurrency Utilities: JSR-166
Task Scheduling Framework: Executor interface replaces direct use of Thread
Callable and Future
Synchronisers
Semaphore, CyclicBarrier, CountDownLatch
Concurrent collections : BlockingQueue
Lock
Atomic
Concurrency Utilities: JSR-166
Task Scheduling Framework: Executor interface replaces direct use of Thread
No more direct Thread invocation
Use myExecutor.execute(aRunnable);
Not new Thread(aRunnable).start();
public interface Executor { void execute (Runnable command); }
Executor Framework for asynchronous execution public interface Executor { void execute (Runnable command); } public interface ExecutorService extends Executor { .. } public class Executors { //Factory methods static ExecutorService newFixedThreadPool(int poolSize); ... } Executor pool = Executors.newFixedThreadPool(5); pool.execute ( runnable ) ;
Creating Executors
Factory methods in the Executors class
public class Executors { static ExecutorService newSingleThreadedExecutor (); static ExecutorService newFixedThreadPool (int poolSize); static ExecutorService newCachedThreadPool (); static ScheduledExecutorService newScheduledThreadPool (); // Other methods not listed }
Thread Pool Example class WebService { public static void main(String[] args) { Executor pool = Executors.newFixedThreadPool(5); ServerSocket socket = new ServerSocket(999); for (;;) { final Socket connection = socket.accept(); Runnable task = new Runnable() { public void run() { new Handler().process ( connection ); } } pool.execute ( task ) ; } } } class Handler { void process (Socket s); }
ExecutorService for Lifecycle Support
ExecutorService supports graceful and immediate shutdown
Schedule execution of Callable or Runnable to run once after a fixed delay
schedule ()
Schedule a Runnable to run periodically at a fixed rate
scheduleAtFixedRate ()
Schedule a Runnable to run periodically with a fixed delay between executions
scheduleWithFixedDelay ()
Submission returns a ScheduledFuture
Can be used to cancel task
ScheduledExecutorService Example ScheduledExecutorService sched = Executors. newSingleThreadScheduledExecutor (); public void runTwiceAnHour(long howLong) { final Runnable rTask = new Runnable() { public void run() { /* Work to do */ } }; final ScheduledFuture<?> rTaskFuture = sched. scheduleAtFixedRate (rTask, 0, 1800, SECONDS); sched. schedule (new Runnable { public void run { rTaskFuture.cancel(true); } }, howLong, SECONDS); }
Synchronize Critical Section
E.g., shared resource is an customer account. Certain methods called by multiple threads.
Hold monitor lock for as short a time as possible .
synchronized double getBalance() { Account acct = verify(name, password); return acct.balance; } Lock held for long time double getBalance() { synchronized (this) { Account acct = verify(name, password); return acct.balance; } } Current object is locked Equivalent to above double getBalance() { Account acct = verify(name, password); synchronized (acct) { return acct.balance}; } Better Only acct object is locked – for shorter time
Locks
Java provides basic locking via synchronized
Good for many situations, but some issues
Single monitor per object
Not possible to interrupt thread waiting for lock
Not possible to time-out when waiting for a lock
Block structured approach
Aquiring multiple locks is complex
Advanced techniques like hand-over-hand locking are not possible
One or more threads wait for a set of threads to complete an action
CyclicBarrier
Set of threads wait until they all reach a specified point
Exchanger
Two threads reach a fixed point and exchange data
BlockingQueue Interface
Provides thread safe way for multiple threads to manipulate collection
Interface BlockingQueue<E> { void put (E o) throws IE; boolean offer (E o) throws IE; boolean offer (E o, long t, TimeUnit u) throws IE; E take () throws IE; E poll () throws IE; E poll (long t, TimeUnit u) throws IE; int drainTo (Collection<? super E> c); int drainTo (Collection<? super E> c, int max); // Other methods not listed }
BlockingQueue Implementations
ArrayBlockingQueue
Bounded queue, backed by an array, FIFO
LinkedBlockingQueue
Optionally bounded queue, backed by linked nodes, FIFO
PriorityBlockingQueue
Unbounded queue
Uses comparator or natural ordering to determine the order of the queue
Blocking Queue Example: 2 private ArrayBlockingQueue messageQueue = new ArrayBlockingQueue<String>(10); Logger logger = new Logger( messageQueue ); public void run() { String someMessage; try { while (true) { /* Do some processing */ /* Blocks if no space available */ messageQueue.put(someMessage) ; } } catch (InterruptedException ie) { } }
Concurrent Collections
ConcurrentMap (interface)
Extends Map interface with atomic operations
ConcurrentHashMap
Fully concurrent retrieval
Tunable concurrency for updates
Constructor takes number of expected concurrent threads
ConcurrentLinkedQueue
Unbounded, thread safe queue, FIFO
CopyOnWriteArrayList
Optimised for frequent iteration, infrequent modifications
Summary
New concurrency features are very powerful
Lots of great features
Take time to learn how to use them correctly
Use them!
For More Information
Garbage Collection
Classic Memory Leak in C
User does the memory management
void service(int n, char** names) {
for (int i = 0; i < n; i++) {
char* buf = (char*) malloc (strlen(names[i]));
strncpy(buf, names[i], strlen(names[i]));
}
// memory leaked here
}
User is responsible for calling free()
User is vulnerable to dangling pointers and double frees.
Garbage Collection
Find and reclaim unreachable objects
Anything not transitively reachable from the application roots:
(thread stacks, static fields, registers.)
Traces the heap starting at the roots
Visits every live object
Anything not visited is unreachable
Therefore garbage
Variety of approaches
Compacting/non-compacting
Algorithms: copying, mark-sweep, mark-compact, etc.
Generational Garbage Collection
Keeps young and old objects separately
In spaces called generations
The weak generational hypothesis
Most new objects will die young, Concentrate effort on young generation
Young Generation Old Generation Object Promotion Object Allocation Track These ( Remembered Set )
Need to keep track of old-to-young pointers
Eventually, have to also collect the old generation
Different GC algorithms for each generation
“ Use the right tool for the job”
Garbage Collection
Garbage collection: Pros
Increased reliability – no memory leaks, no dangling pointers
Eliminates entire classes of (Pointer) bugs , no segmentation fault, no double frees
Improved developer productivity
True memory leaks are not possible
Still possible for an object to be reachable but not used by the program
Best described as unintentional object retention , Can cause OutOfMemoryError
Happens less often than in C, and easier to track down
Cons
Pauses
May require tuning – but auto-tuning is getting better!!!
Incremental Garbage Collection
decreases/minimizes GC disruption
GC works at the same time as the application
Short stop do a little work go back
Garbage Collection
Myths about garbage collection abound
Myth: Allocation and garbage collection are slow
In JDK 1.0 , they were slow (as was everything else)
Memory management (allocation + collection) in Java is often significantly faster than in C
Cost of new Object() is typically ten machine instructions
It's just easier to see the collection cost because it happens all in one place
Early performance advice suggested avoiding allocation
Bad idea!
Alternatives (like object pooling ) are often slower , more error prone , and less memory-efficient
Object Allocation (1/2)
Typically, object allocation is very cheap!
10 native instructions in the fast common case
C/C++ has faster allocation? No!
Reclamation of new objects is very cheap too!
Young GCs in generationa l systems
So
Do not be afraid to allocate small objects for intermediate results
Generational GCs love small, short-lived objects
Object Allocation (2/2)
We do not advise
Needless allocation
More frequent allocations will cause more frequent GCs
We do advise
Using short-lived immutable objects instead of long-lived mutable objects
Using clearer, simpler code with more allocations instead of more obscure code with fewer allocations
Large Objects
Very large objects are:
Expensive to allocate (maybe not through the fast path)
Expensive to initialize (zeroing)
Can cause performance issues
Large objects of different sizes can cause fragmentation
For non-compacting or partially-compacting GCs
Avoid if you can
And, yes, this is not always possible or desirable
Object Pooling (1)
Legacy of older VMs with terrible allocation performance
Remember
Generational GCs love short-lived, immutable objects…
Unused objects in pools
Are like a bad tax, the GC must process them
Safety
Reintroduce malloc/free mistakes
Scalability
Must allocate/de-allocate efficiently
synchronized defeats the VM’s fast allocation mechanism
Object Pooling (3/3)
Exceptions
Objects that are expensive to allocate and/or initialize
Objects that represent scarce resources
Examples
Threads pools
Database connection pools
Use existing libraries wherever possible
Memory Leaks?
But, the GC is supposed to fix memory leaks!
The GC will collect all unreachable objects
But, it will not collect objects that are still reachable
Memory leaks in garbage collected heaps
Objects that are reachable but unused
Unintentional object retention
Memory Leak Types
“Traditional” memory leaks
Heap keeps growing , and growing, and growing …
OutOfMemoryError
“Temporary” memory leaks
Heap usage is temporarily very high , then it decreases
Bursts of frequent GCs
Memory Leak Sources
Objects in the wrong scope
Lapsed listeners
Exceptions change control flow
Instances of inner classes
Metadata mismanagement
Use of finalizers/reference objects
Objects in the Wrong Scope (1/2)
Below, names really local to doIt()
It will not be reclaimed while the instance of Foo is live
class Foo {
private String[] names ;
public void doIt (int length) {
if (names == null || names.length < length)
names = new String[length];
populate(names);
print(names);
}
}
Objects in the Wrong Scope (2/2)
Remember
Generational GCs love short-lived objects
class Foo {
public void doIt(int length) {
String[] names = new String[length];
populate(names);
print(names);
}
}
Memory Leak Sources
Objects in the wrong scope
Lapsed listeners
Exceptions change control flow
Instances of inner classes
Metadata mismanagement
Use of finalizers/reference objects
Exceptions Change Control Flow (1/2)
Beware
Thrown exceptions can change control flow
try {
ImageReader reader = new ImageReader();
cancelButton.addActionListener(reader);
reader.readImage(inputFile);
cancelButton.removeActionListener(reader);
} catch (IOException e) {
// if thrown from readImage(), reader will not
// be removed from cancelButton's listener set
}
Exceptions Change Control Flow (2/2)
Always use finally blocks
ImageReader reader = new ImageReader();
cancelButton.addActionListener(reader);
try {
reader.readImage(inputFile);
} catch (IOException e) {
...
} finally {
cancelButton.removeActionListener(reader);
}
Memory Leak Sources
Objects in the wrong scope
Lapsed listeners
Exceptions change control flow
Instances of inner classes
Metadata mismanagement
Use of finalizers/reference objects
Metadata Mismanagement (1/2)
Sometimes, we want to:
Keep track of object metadata
In a separate map
class ImageManager {
private Map<Image,File> map =
new HashMap<Image,File>();
public void add(Image image, File file) { ... }
public void remove(Image image) { ... }
Public File get(Image image) { ... }
}
Metadata Mismanagement (2/2)
What happens if we forget to call remove (image)?
never be removed from the map
Very common source of memory leaks
We want:
purge the corresponding entry when the key is not reachable…
That’s exactly what a WeakHashMap does
purge the corresponding entry
private Map<Image,File> map =
new Weak HashMap<Image,File>();
Some Memory Management Myths
Myth: Explicitly nulling references helps GC
Rarely helpful
Unless you are managing your own memory
Can be harmful to correctness or performance
Myth: Calling System.gc() helps GC
Triggers full collection – less efficient
Can be a huge performance loss
Myth: Avoid object allocation
Allocation in Java is lightning fast
Avoidance techniques (e.g., pooling ) are very tricky to get right
Local Variable Nulling
Local variable nulling i s n ot necessary
The JIT can do liveness analysis
void foo() {
int[] array = new int[1024];
populate(array);
print(array); // last use of array in method foo()
array = null; // unnecessary!
// array is no longer considered live by the GC
...
}
Some Memory Management Myths
Myth: Finalizers are Java's idea of destructors
Finalizers are rarely needed and very hard to use correctly!
Should only be used for native resources
Adds significant work to GC , has significant performance effect
Instead, use finally blocks to release resources
Resource r = acquireResource();
try {
useResource(r); } finally {
releaseResource(r);
}
Note resource acquisition is outside the try block
Use for file handles, database connections, etc
Virtual Machine Smart Tuning
How “Smart Tuning” Works
Provide good “ out of the box ” performance without hand tuning
Determine type of machine JVM is running on configure Hotspot appropriately
Server machine
Larger heap, parallel garbage collector , and server compiler
Client machine
Same as 1.4.2 ( small heap , serial garbage collector, and client compiler
“ Smart Tuning”
Dynamically adjust Java HotSpot VM software environment at runtime
Adaptive Heap Sizing policy
Simple tuning options based on application requirements not JVM internals
JDK 6 Component JSRs 202: Class File Update 199: Compiler API 269: Annotation Processors 260: Javadoc ™ Tag Update Ease of Development 221: JDBC ™ 4.0 223: Scripting 105: XML Digital Signature 173: Streaming API for XML XML 222: JAXB 2.0 250: Common Annotations 181: WS Metadata Web Services 224: JAX-WS 2.0 See JSR 270 at http://jcp.org
The JDK 6 Top 10
Scripting
Web Services
Database (JDBC 4.0, Java DB)
More Desktop APIs
Monitoring and Management
Compiler Access
Pluggable Annotations
Desktop Deployment
Security
Quality, Compatibility, Stability
Resources and Summary
For More Information (1/2)
Memory management white paper
http://java.sun.com/j2se/reference/whitepapers/
Destructors, Finalizers, and Synchronization
http://portal.acm.org/citation.cfm?id=604153
Finalization, Threads, and the Java Technology Memory Model
0 comments
Post a comment