|
COOTS '01 Paper   
[COOTS '01 Tech Program Index]
Making Java Applications Mobile or Persistent
AbstractToday, mobility and persistence are important aspects of distributed applications. They have many fields of use such as load balancing, fault tolerance and dynamic reconfiguration of applications. In this context, the Java virtual machine provides many useful services such as dynamic class loading and object serialization which allow Java code and objects to be mobile or persistent. However, Java does not provide any service for the mobility or the persistence of control flows (threads), the execution state of a Java program remains inaccessible.We designed and implemented new services that make Java threads mobile
or persistent. With these services, a running Java thread can, at an arbitrary
state of its execution, migrate to a remote machine or be checkpointed
on disk for a possible subsequent recovery.
We integrated these services into the JVM, so they provide reasonable
and competitive performance figures without inducing an overhead on JVM
performance. Finally, we experimented a dynamic reconfiguration tool based
on our mobility service and applied to a running distributed application.
1. IntroductionToday, mobility and persistence are important aspects of distributed applications and have several fields of use [Milojicic99] [Ambler99]. Application mobility can be used to dynamically balance the load between several machines in a distributed system [Nichols87], to reduce network traffic by moving clients closer to servers [Douglis92], to dynamically reconfigure distributed applications [Hofmeister93], to implement mobile agent platforms [Chess95] or as a machine administration tool [Oueichek96]. Application persistence can be used for fault tolerance [Wojcik95] or for application debugging.Distributed applications development is an important research direction in computing systems. In this context, the object paradigm has proven to be well suited to distributed applications development and the Java Virtual Machine (JVM) is now considered as a reference platform [Gosling96]. The Java compiler produces bytecode, an intermediate code that is interpreted by the JVM. Today, the JVM is ported on almost every platform and can therefore be viewed as a universal machine. In order to facilitate the development of distributed applications, the JVM provides several services [Sun00a] among which: Therefore, Java provides useful services for the mobility and the persistence of code and data. However, Java does not provide any service enabling the mobility and the persistence of applications during their execution. Thus, if a running Java application migrates to a new location, only using object serialization and dynamic class loading, the execution state of the application is lost. In other words, when arriving on its new location, the migratory application can access to its code and its re-actualized data but it has to restart the execution from the beginning. Consequently, the provided Java services are not sufficient for either enabling dynamic load balancing of distributed Java executions or allowing the state of running applications to be checkpointed and then recovered. We designed and implemented new services that make Java threads, i.e. executions, mobile or persistent. With these services, a running Java thread can, at an arbitrary state of its execution, migrate to a remote machine or be checkpointed on disk for a possible subsequent recovery. Our java.lang.threadpack Java package provides several primitives, among which go performs thread migration, store is used for thread checkpointing and load for thread recovery. Therefore migrating a Java thread is simply performed by the call of the go primitive, by the thread itself or by an external thread. In other words, the migration or the checkpointing of a thread can be initiated by the thread itself or by another thread. We integrated these services into the JVM, so they provide acceptable performance figures without inducing an overhead on JVM performance. Finally, we experimented with a prototype implementation a dynamic reconfiguration tool based on our mobility service and applied to a running distributed application. The rest of this paper consists of three main parts. We first describe our service for capturing/restoring Java thread state in section 2 and then present the services of thread mobility and thread persistence in section 3. In sections 4 and 5, we respectively present performance figures and describe some experiments that we performed with our services. Finally, we discuss related work and present our conclusions and future directions in section 6 and 7.
2. Thread state capture/restoration serviceBoth services allowing the mobility and the persistence of Java threads are based on a common service: a thread state capture/restoration service. We first describe the representation of a thread state in the JVM and then present the design principles of our capture/restoration service and its implementation details.2.1. Java thread stateThe JVM can support the concurrent execution of several threads [Lindholm96]. The state of a Java thread is illustrated by figure 1, it consists of three main data structures:
2.2. Design of the capture/restoration serviceHere are the design principles and the design decisions of our Java thread state capture/restoration service.2.2.1. Design principlesThe thread state capture/restoration service enables, on the one hand, the capture of the current state of a running thread, and on the other hand, the restoration of a previously captured state in a new thread: the new thread starts running at the point at which the execution of the previous thread was interrupted.Thread state capture consists in interrupting the thread during its execution and extracting its current state. The extraction amounts to build a data structure (a Java object) containing all information necessary for restoring the Java stack, the heap and the method area associated with the thread. To build such a data structure, the Java stack associated with the thread is scanned in order to identify the objects and the classes that are referenced from the stack. After state capture, the resulting data structure can be serialized and sent to another virtual machine in order to implement mobility or it can be stored on disk for persistence purpose. One of our motivations was to provide a generic service which allows the implementation of various capture policies. Consequently we rely on Java object serialization and dynamic class loading features in order to capture respectively the heap and the method area. The restoration of a thread consists first in creating a new thread and initializing its state with a previously captured state. After that, the Java stack, the heap and the method area associated with the new thread are identical to those associated with the thread whose state was previously captured. Finally, the new thread is started, it resumes the execution of the previous thread. 2.2.2. Design decisionsThere were mainly two problems for designing a Java thread state capture/restoration service. The first issue is to have access to the state of Java threads, a state that is internal to the JVM. The second issue is that the state of Java threads is not portable on heterogeneous architectures.2.2.2.1. Non accessible stateThe state of Java threads is internal to the JVM. This state is not accessible by Java programs and can therefore not be directly captured. In order to allow the capture of threads state, we extended the JVM and externalized the state of Java threads.2.2.2.2. Non portable stateUnlike the heap and the method area that consist of information portable on heterogeneous architectures (Java objects and Java classes), the Java stack is a native data structure (C structure). The representation of the information contained in the Java stack depends on the underlying architecture. The thread state capture service must translate this non portable data structure (C structure) to a portable data structure (Java object).Translating the Java stack into a portable data structure consists more precisely in translating the native values of local variables and partial results (figure 2) into Java values. This translation requires the knowledge of the types of the values. But the Java stack does not provide any information about the types of the values it contains: a four bytes word may represent a Java reference as well as an int value or a float value. The thread state capture service must recognize the types of the values contained in the Java stack. We propose two approaches for type recognition: We first implemented the approach based on type recognition at runtime [Bouchenak00] and then implemented the approach based on type recognition at capture time*. In this paper, we focus our attention on the design principles of our services and do not tackle the implementation details.
* The evaluation presented in section 4 concerns the first approach 2.3. Implementation of the capture/restoration serviceOur Java thread state capture/restoration service was integrated to the Java 2 SDK (formerly called JDK 1.2) [Sun00a]. Our new Java package, called java.lang.threadpack, provides many classes such as the ThreadState class whose instances represent the state of Java threads and the ThreadStateManagement class that provides the necessary features for capturing and restoring Java threads state.Figure 3 illustrates a part of the application programming interface (API) of the ThreadStateManagement class. The capture method allows the capture of the current state of a Java thread, the captured state is returned as a result of this method, as a ThreadState object. Symmetrically, the restore method creates a new Java thread, initializes its state with the ThreadState argument, starts the new thread and returns it as a result of the method. The new thread resumes the execution of the thread whose state was previously captured and passed as an argument of the restore method.
The captureAndSend and receiveAndRestore methods are generic and can specialize the capture and restoration operations to application needs. Besides capturing the state of a Java thread, the captureAndSend method allows the programmer to specify the way the captured state is handled: the captured state can for example be sent to a remote machine for a mobility purpose, it can be stored on disk in the case of application persistence, etc. The specialization of the handling of the captured state is specified by the second argument of the captureAndSend method. In fact, this argument implements our SendInterface interface and so provides a sendState method that is called by our captureAndSend method, just after the capture of the thread state. The third argument of the captureAndSend method is a boolean that specifies if the thread whose state is captured is stopped or resumed. This argument is for example set to true in the case of thread migration and is set to false for remote thread cloning. Symmetrically, the receiveAndRestore method specifies the way a thread state is received before it is restored: the state can for example be received from a remote machine, it can be read from disk, etc. The specialization of the way the thread state is received is possible thanks to the argument of the receiveAndRestore method: this argument implements our ReceiveInterface interface and so provides a receiveState method that is called by our receiveAndRestore method, just before the restoration operation.
3. Thread mobility and thread persistence servicesBesides our system service for capturing/restoring the state of Java threads, we provide higher-level services for the mobility and the persistence of Java applications.Making an application mobile is the action of moving an application, during its execution, from one node to another: the application starts running, on the new node, at the point at which the execution was interrupted on the first node. Therefore, making a Java application mobile consists in making the underlying Java thread mobile. In the case of a multi-threaded application, the whole group of threads has to be moved. Making a thread mobile is the action of capturing the current state
of the thread, sending this state to a target machine and restoring the
state in a new thread on the target machine: the new thread resumes the
execution in the state left by the original thread.
In the same way, application persistence consists first in saving the current state of a running application on stable storage (disk). The saved state can subsequently be restored in order to resume the execution of the application. Therefore, making a Java application persistent consists in making the underlying Java thread(s) persistent. Making a thread persistent is, first, the action of capturing the current state of the thread and saving it on disk and then, the ability of restoring the saved state in a new thread: the new thread resumes the execution of the previous thread. Our MobileThreadManagement class belongs to the java.lang.threadpack package and provides services necessary for the mobility of Java threads. Figure 4.a illustrates a part of the application programming interface of this class. The go method allows the transfer of a running Java thread to a Java virtual machine identified by an IP address and a port number. And the arrive method enables the reception of a Java thread coming from a machine specified by an IP address and a port number.
The go and arrive methods are implemented using respectively the captureAndSend and receiveAndRestore generic methods (see section 2.3). The go method is implemented as follows:
The arrive method is implemented as follows: We can also imagine go and arrive methods that rely on the Wireless Application Protocol instead of IP in order to perform thread migration between JVM installed on wireless hosts [WAPFactory00].
In the same way, the PersistentThreadManagement provides several services for the persistence of Java threads. A part of its application programming interface is illustrated by figure 5. The store method saves the current state of a Java thread in a file specified by a name and the load method restores a Java thread from a state saved in a file identified by a name. These two methods are also implemented using our captureAndSend and receiveAndRestore generic methods. Finally, the MobileThreadManagement and PersistentThreadManagement classes are two possible adaptations of our generic service of Java thread state capture/restoration. In the same way and for a particular application, our generic service can be adapted to build tools that meet application's needs.
4. EvaluationThis section first presents the performance figures of our thread state capture/restoration service. The cost of migrating a Java thread between two machines and the cost of checkpointing/recovering a thread are then presented. Finally, a comparison between the results of benchmarking our extended JVM and the standard JVM is described. Our evaluation environment is as follows:
4.1. Basic costsThe time spent in capturing/restoring a Java thread state depends on the size of the state at capture time. The size of a Java thread depends on the number and the size of the frames pushed onto the Java stack associated with the thread. In the following, we focus our attention on the influence of the number of frames on the cost of our services. In order to vary the number of frames pushed onto thread's Java stack, we used a recursive program (the factorial function).
Figure 6 describes the variation of the cost of a thread state capture operation according to the number of frames on thread's Java stack at capture time. The cost of a capture operation is less than 1 ms when the number of frames is lower than 10. This cost reaches 2 ms when the number of frames is 20 and 9 ms when the number of frames is 80. Figure 7 presents the cost of a thread state restoration operation when varying the number of frames on thread's Java stack at capture time. The curve shows that the cost of a restoration operation is less than 1 ms when the number of frames is lower than 80. Finally, the costs of the capture and the restoration of Java thread's state are acceptable, especially in the case of threads with few frames on the Java stack. 4.2. Evaluation of thread migrationWe measured the cost of a Java thread migration based on our thread mobility service. In figure 8, the solid curve represents the variation of the cost of a Java thread migration operation according to the number of frames on thread's Java stack at migration time. The dotted curve represents the cost of a thread state transfer between two machines when varying the number of frames on thread's Java stack.
The cost of a thread migration linearly varies from 100 ms to 600 ms when the number of frames on the thread's stack is between 1 and 100. This cost may seem significant but it is mainly due to the cost of thread state transfer, as shown by the two almost superimposed curves. In fact, thread migration consists in capturing thread's state, sending this state to a destination machine and restoring the state in a new thread on the destination machine. So state transfer represents 98% of the total cost of thread migration. On the other hand, the transfer of a thread state to a destination machine consists in first serializing the state object in order to translate the object graph to a byte array, then transferring the resulting array of bytes over the network to the destination machine and finally de-serializing the byte array on the destination machine in order to rebuild the object graph. The state transfer time can partly be reduced using Java externalization rather than serialization. Externalization allows the application programmer to write its own object transfer policy by only saving information necessary for rebuilding object graphs. Externalization may be until 40% faster than serialization [Sun00c]. 4.3. Evaluation of thread checkpointing and recoveryBesides thread migration, we also measured the cost of checkpointing a running Java thread and saving its state on disk and the cost of recovering an execution from a state previously stored on disk. In figure 9, the solid curve represents the variation of the cost of a Java thread checkpointing operation according to the number of frames on the thread's Java stack at checkpointing time. The dotted curve represents the cost of writing a thread state on disk according to the number of frames on the thread's Java stack. In figure 10, the solid curve represents the cost of a Java thread recovery and the dotted curve illustrates the cost of reading a thread state from disk.
We notice, on the one hand, that the cost of thread checkpointing and the cost of thread recovery increase linearly when the number of frame on the thread's Java stack increases. On the other hand, 97% of the time of thread checkpointing is spent in writing thread's state on disk and 99% of the time of thread recovery is spent in reading thread's state from disk. As explained in section 4.2, the costs of serialization/de-serialization can be decreased by using externalization. The performance of thread checkpointing can also be improved by performing asynchronous disk writing. 4.4. Benchmarking the JVMOur thread mobility and thread persistence services were integrated into the JVM. In order to evaluate the performance of our extension of the JVM, we compared them to the performance figures of the standard JVM. We used two benchmarks: the Embedded CaffeineMark 3.0 general Java benchmark [Pendragon99] and the SciMark 2.0 numeric Java benchmark [Pozo00]. In order to measure JVM performance, the benchmarks were run by disabling JIT compilation.
Embedded CaffeineMark consists of 6 tests: finding prime numbers, loops, logic tests, String and Float tests and method calls. The overall score is the geometric mean of the individual scores, i.e., it is the 6th root of the product of all the scores. The score for each test is proportional to the number of times the test was executed divided by the time taken to execute the test, i.e. a higher number represents a better score. Figure 11 presents the results of benchmarking the standard JDK 1.2.2 and our extended JDK 1.2.2. It shows that our extension does not induce any loss of performance on the JVM. SciMark 2.0 is a Java benchmark for scientific and numerical computing. It consists of five computational kernels: Fast Fourier Transform (FFT), Jacobi Successive Over-relaxation (SOR), Monte Carlo integration, dense LU matrix factorization and Sparse matrix-multiply. The kernels are chosen to provide an indication of how well the underlying JVM performs on applications utilizing these types of algorithms. The problems sizes are purposely chosen to be small in order to isolate the effects of memory hierarchy and focus on internal JVM and CPU issues. This benchmark reports a composite score in approximate Mflops (Millions of floating point operations per second). Figure 12 shows the performance figures resulting from benchmarking the standard JDK 1.2.2 and our extended JDK 1.2.2. It shows that our extension does not induce any loss of performance on the JVM.
5. ExperimentationIn this section, we describe two experiments that use our mobility service. The first experiment shows the usefulness of strong mobility and the second experiment shows how to build a dynamic reconfiguration tool on top of our mobility service. Finally, we discuss some implementation issues and solutions.5.1. Strong mobility: Mobile recursive FractalTwo degrees of application mobility can be distinguished: weak mobility and strong mobility [Fuggetta98]. With weak mobility, only data state information and application’s code are transferred. Therefore, on the new location, the mobile application has its actualized data but restarts execution from the beginning. With strong mobility, the code of the application and the state of data and execution are transferred: the application on the destination location resumes the execution at the point where it was interrupted on the source location.The usage of weak or strong mobility depends on application’s needs. Let’s consider a recursive Java application. The recursive calls are translated by a succession of frames on the Java stack associated with the underlying thread. How is this application made mobile?
We considered a recursive graphical Java application: the Dragon fractal curve where a small dragon appears at a certain depth of recursion [Mandelbrot75]. We implemented a Java Dragon application and used our thread mobility service in order to move the application, when it is running, between several machines. Figure 13 illustrates this experiment. The Dragon application is first started on a first machine, then moved to a second machine where it resumes its execution and finally moved to a third machine where it finishes its execution. The transfer of the thread calculating the fractal is performed by an external thread that calls the go method of our MobileThreadManagement class. 5.2. Dynamic reconfiguration: Mobile TalkIn this section, we describe how our mobility service can be combined with other Java services (object serialization, dynamic class loading) in order to build a dynamic reconfiguration tool.We consider a Talk application where two remote users exchange messages. Initially, each user starts an instance of the Talk application on its personal computer with a graphical user interface. Each user has two communication channels: an input channel to receive messages from the remote user and an output channel to send messages to the remote user. During the talk, one of the users decides to transfer its application to a minimal host with limited physical characteristics (a mobile phone for example) and to resume its execution. This dynamic reconfiguration of the Talk application is illustrated by figure 14 and has the following requirements:
To transfer the running mobile Talk application to a new host, our mobility service can be used: it takes the current state of the application into account. To transfer the application to a mobile phone, the mobility service must use the Wireless Application Protocol (WAP) [WAPfactory 00]. To tackle the problems of communication channels and user interface, the Java serialization and dynamic class loading services are adapted. In fact, our mobility service relies on both serialization and dynamic class loading to respectively transfer the objects and the classes used by the application at migration time. These two features can be specialized as follows: Finally, the combination of our mobility service, the serialization and the dynamic class loading enables the building of a complete dynamic reconfiguration tool. This application has been experimented with a prototype implementation on our extended JDK 1.2.2. A port of our services to the K Virtual Machine, a lightweight JVM, is planned [Sun00b]. 5.3. DiscussionIn this section, we discuss some issues encountered when implementing thread mobility and thread persistence. Let's focus our attention on the mobility of a thread:We now tackle each of these issues and propose possible solutions. What happens if a thread moves from a source host to a destination host while it is using objects shared with other threads on the source host? A first solution consists in replicating the shared object and transferring it with the mobile thread [Garcia-Molina86]. In this case, the consistency of the replicas must be managed. Another solution to the problem of shared objects is to use proxies on the destination host in order to allow remote access to shared objects. A problem of object availability occurs if the source host crashes [Chou83]. How are the communication channels connecting several threads handled when one of these threads moves to a new host? A first approach consists in using proxies on the destination host in order to access the communication channels remotely. A problem of channel availability occurs if the source host crashes. Another approach consists in closing the channels on the source host and recreating them on the destination host. In this case, messages in transit must be redirected to the new location and the naming of the new channels must be actualized on other hosts. What happens if a thread that belongs to a multi-threaded application
move to a new host? The thread can move alone to the destination host and
communicate with the other threads remotely, or it can move with all the
other threads or with a sub-set of them.
6. Related workMany systems have been developed providing process mobility and persistence, considering either homogeneous or heterogeneous processor architectures. There are a number of surveys discussing these features [Milojicic97] [Deconinck93]. Both mobility and persistence of control flows are based on a mechanism that enables the capture and the restoration of executions’ state. Let’s focus our attention on such mechanisms in the Java environment.Three main approaches to address the problem of capturing/restoring the state of Java threads are distinguished: an explicit approach, an implicit approach based on a pre-processor of the application code and an implicit approach based on an extension of the JVM. In the first approach, which we call explicit management, the programmer of an application has to entirely manage the capture and the restoration of the state of his application. For this purpose, the programmer has to explicitly add supplementary code in fixed points of his program and usually has to manage his own program counter. The added code manages a backup object in which information relative to the state of the application is stored. The backup object is then used in order to restore the application execution. When restoring the state of the application, the first statement of the program is a branch to the point where the program must continue. This approach is not flexible and implies a modification of the application itself if new backup points are added. This approach is used in most of applications based on mobile agent platforms [Chess95] that provide weak mobility, such as Aglets [IBM96] and Mole [Baumann98]. The two other approaches, which we call implicit, provide a transparent service for capturing/restoring thread state. The service is independent from the application code and is provided as a function that may be called by the application itself or by an external application. These two approaches differ by their implementation:
7. Conclusion and future workSince the Java virtual machine does not provide any access to the state of Java threads, we designed and implemented a new service for the capture and restoration of thread state. Our capture/restoration service is generic: we used it as a basis for the implementation of thread mobility and thread persistence services. With these services, a running Java thread can, at an arbitrary state of its execution, migrate to a remote machine or be checkpointed on disk and then recovered. In addition, the migration or the checkpointing of a thread can be initiated by the thread itself or by another thread.Our services were integrated into the JVM, so they provide acceptable
performance figures without inducing overhead on JVM performance. Finally,
we experimented with a prototype implementation a dynamic reconfiguration
tool based on our mobility service and applied to a running distributed
application.
At the present time, we are considering the usage of our services in real world applications such as dynamic load balancing in distributed systems and the integration of our services into distributed Java virtual machines. We also plan to port our services to the K Virtual machine, the lightweight JVM, in order to make them available on small devices such as phones and PDA [Sun00b].
AcknowledgmentsI would like to thank Sacha Krakowiak and Jacques Mossière for providing many useful suggestions that significantly improved this paper.
References[Ambler99] S. W. Ambler. The Design of a Robust Persistence Layer For Relational Databases. AmbySoft Inc. White Paper, October 1999. http://www.ambysoft.com/persistenceLayer.html[Baumann98] J. Baumann, F. Hohl, M. Straber and K. Rothermel. Mole - Concepts of Mobile Agent System. WWW Journal, Special issue on Applications and Techniques of Web Agents, volume 1, no 3, 1998. http://mole.informatik.uni-stuttgart.de/ [Bouchenak00] S. Bouchenak and D. Hagimont. Pickling threads state in the Java system. Proceedings of 33rd International Conference on Technology of Object-Oriented Languages (TOOLS Europe’2000), Mont-Saint-Michel, France, June 2000. http://sirac.inrialpes.fr/~bouchena [Chess95] D. Chess, C. Harrison and A. Kershenbaum. Mobile Agents: Are They a Good Idea? T.J. Watson Research Center White Paper, IBM Research Division, March 1995. http://www.research.ibm.com/iagents/ publications.html [Chou83] T. C. K. Chou and J. A. Abraham. Load Redistribution under Failure in Distributed Systems. IEEE Transactions on Computers, volume 32, no 9, September 1983. [Deconinck93] Geert Deconinck, Johan Vounckx, Rudi Cuyvers and Rudy Lauwereins. Survey of Checkpointing and Rollback Techniques. Technical Report, Katholieke Universiteit Leuven, Belgium, June 1993. [Douglis92] F. Douglis and B. Marsh. The Workstation as a Waystation: Integrating Mobility into Computing Environment. The Third Workshop on Workstation Operating System (IEEE), Key Biscayne, Florida, USA, April 1992. http://www.douglis.org/fred [Fuggetta98] A. Fuggetta, G. P. Picco and G. Vigna. Understanding Code Mobility. IEEE Transactions on Software Engineering, volume 24, no 5, 1998. http://www.cs.ucsb.edu/~vigna/listpub.html [Fünfrocken98] S. Fünfrocken. Transparent Migration of Java-based Mobile Agents(Capturing and Reestablishing the State of Java Programs). Proceedings of Second International Workshop Mobile Agents 98 (MA'98), Stuttgart, Germany, September 1998. http://www.informatik.tu-darmstadt.de/~fuenf [Garcia-Molina86] H. Garcia-Molina. The Future of Data Replication. Symposium on Reliability in Distributed Software and Database Systems, Los Angeles, California, USA, January 1986. [Gosling96] J. Gosling and H. McGilton. The Java Language Environment. Sun Microsystems White Paper, May 1996. http://java.sun.com/docs/white [Hofmeister93] C. Hofmeister and J. M. Purtilo. Dynamic Reconfiguration in Distributed Systems : Adapting Software Modules for replacement. Proceedings of the 13th International Conference on Distributed Computing Systems, Pittsburgh, USA, May 1993. [IBM96] IBM Tokyo Research Labs. Aglets Software Development Kit, 1996. http://www.trl.ibm.co.jp/aglets [Lindholm96] T. Lindholm and F. Yellin. Java Virtual Machine Specification. Addison Wesley, 1996. [Mandelbrot75] B. Mandelbrot . Les Objets fractals : forme, hasard et dimension. Flammarion, 1975. [Milojicic97] D. Milojicic, F. Douglis, Y. Paindaveine, R. Wheeler and S. Zhou. Process Migration. TOG Research Institute Technical Report. March 1999. http://www.camb.opengroup.org/RI/java/moa [Milojicic99] D. Milojicic, F. Douglis and R. Wheeler. Mobility: Processes, Computers and Agents. Addison Wesley, February 1999. [Nichols87] D. A. Nichols. Using Idle Workstations in a Shared Computing Environment. Proceedings of the Eleventh ACM Symposium on Operating Systems Principles, Austin, Texas, USA, November 1987. [Oueichek96] I. Oueichek. Conception et réalisation d'un noyau d'administration pour un système réparti à objets persistants. Thèse de Doctorat, Institut National Polytechnique de Grenoble, France, October 1996. [Pendragon99] Pendragon Software Corporation. CaffeineMarkTM 3.0. CaffeineMark 3.0 documentation, 1999. http://www.pendragon-software.com/pendragon/cm3 [Pozo00] R. Pozo and B. Miller. SciMark 2.0. SciMark 2.0 documentation, 2000. http://math.nist.gov/scimark2 [Ranganathan97] M. Ranganathan, A. Acharya, S. D. Sharma and J. Saltz. Network-aware Mobile Programs. Proceedings of the USENIX Annual Technical Conference, Anaheim, California, USA, January 1997. http://searchpdf.adobe.com/proxies/0/38/54/9.html [Sakamoto00] T. Sakamoto, T. Sekiguchi, A. Yonezawa. Bytecode Transformation for Portable Thread Migration in Java. Proceedings of Second International Workshop Mobile Agents 2000 (MA'2000), Zurich, Switzerland, September 2000. http://web.yl.is.s.u-tokyo.ac.jp/~takas/ [Suezawa00] T. Suezawa. Persistent Execution State of a Java Virtual
Machine. Proceedings of the ACM 2000 Java Grande Conference, San Francisco,
California, USA, June 2000. http://www.ifi.unizh.ch/staff/suezawa/
[Sun00a] Sun Microsystems. Java 2 SDK, Standard Edition. Sun Microsystems documentation, 2000. http://java.sun.com/products/jdk/1.2 [Sun00b] Sun Microsystems. Java 2 Platform Micro Edition (J2ME) Technology for Creating Mobile Devices. Sun Microsystems White Paper, 2000. http://java.sun.com/products/cldc [Sun00c] Sun Microsystems. Improving Serialization Performance with Externalizable. Technical Tips, Sun Microsystems, April 2000. http://developer.java.sun.com/developer/TechTips/2000/tt0425.html [Truyen00] E. Truyen, B. Robben, B. Vanhaute, T. Coninx, W. Joosen and P. Verbaeten. Portable Support for Transparent Thread Migration in Java. Proceedings of Second International Workshop Mobile Agents 2000 (MA'2000), Zurich, Switzerland, September 2000. http://www.cs.kuleuven.ac.be/~eddy/research.html [WAPfactory00] WAPfactory. Wap.com. 2000. http://www.wap.com/ [Wojcik95] Z. M. Wojcik and B. E. Wojcik. Optimal Algorithm for Real-Time
Fault Tolerant Distributed Processing Using Checkpoints. Informatica, volume
19, no 1, February 1995.
|
This paper was originally published in the
Proceedings of the 6th USENIX Conference on Object-Oriented Technologies and Systems,
January 29-February 2, 2001, San Antonio, Texas, USA.
Last changed: 4 Jan. 2002 ml |
|