This blog post follows on from Some of the mysteries of Java shared classes and gives some CPU figures.
This should help you with any of the Java applications running on z/OS, such as z/OSMF, z/OS Connect, MQWEB, RSEAPI, and ZOWE.
I ran the scenarios on z/OS on zPDT running on my Ubuntu Linux machine, and so the figures are nothing like you may expect on a real z/OS machine – but my figures should show you the potential.
Topics covered:
- Overview of Java shared classes
- Measurements
- Scenarios
- Analysis of the results
- Setting up to use the shared classes
- Where do you harden the cache to?
- What happens if I change my Java program?
- What happens internally?
- Should I use .class files or package the .class files into a .jar files?
- Should I use of BPXBATCH or BPXBATSL?
- Problems I experienced while setting this up.
Overview of Java shared classes
With Java shared classes support, as a Java program starts, and reads the jar and class files and also copies them into memory somewhere. Successive start can use the in memory copy and avoid the read for disk and initial processing.
You can save the in-memory copy to disk, and restore this disk copy to memory, for example across IPLs.
Measurements
I measured the CPU user from the address space once the system was started
The Java program provides a high level trace. I note the time difference between the first message and the “I am up” message
Scenarios
I used three scenarios
- IPL and start Java program with no share classes
- Enable shared classes
- IPL and restore the shared classes, and start the program
No shared classes
| Scenario | CPU | Duration seconds |
| First run after IPL | 394 | 172 |
| Second run | 425 | 183 |
Enable shared classes
I enabled shared classes by using the Java option
-Xshareclasses:verbose,name=rseapi,cachedir=/tmp/,groupAccess,nonpersistent,nonfatal,cacheDirPerm=0777″
| Scenario | CPU | Duration seconds |
| run after shared classes enabled | 500 | 200 |
| Second run after shared classes enabled | 292 | 116 |
| Third run after shared classes enabled | 251 | 81 |
IPL and restore snapshot
| Scenario | CPU | Duration seconds |
| First run after (IPL and restore snapshot ) | 274 | 99 |
| Second run | 272 | 121 |
| Third run | 279 | 116 |
| Fourth run | 264 | 111 |
Analysis of the results
Using the shared classes saved CPU in the region of 25% and reduced the elapsed time by about a half.
The first time the Java program runs and creates the shared class data has a higher CPU cost, and increased elapsed time. The savings of CPU and elapsed time when the shared cache is reused outweighs this one time cost.
Observation
It appears that each time you restart using shared classes the CPU drops. I think this is due to the optimisation being done on the classes, but it may be some totally different effect – or it may just be co-incidence!
Setting up to use the shared classes
I added two job steps to my Java program JCL
Before – restore the share classes cache from the backup copy
// EXPORT SYMLIST=* // SET J='/usr/lpp/java/J8.8_64/J8.0_64/bin' // SET C='/tmp/' // SET N='rseapi' // SET V='restoreFromSnapshot' // SET Q='cacheDirPerm=0777,groupAccess' //RESTORE EXEC PGM=BPXBATCH,REGION=0M,PARMDD=PARMDD //PARMDD DD *,SYMBOLS=(JCLONLY) SH &J/java -Xshareclasses:cacheDir=&C,name=&N,&V,&Q /*
If the in-memory cache exists you get message
JVMSHRC726E Non-persistent shared cache “rseapi” already exists. It cannot be restored from the snapshot.
After – save the shared class cache to disk
// SET V='snapshotCache' // SET J='/usr/lpp/java/J8.8_64/J8.0_64/bin' //SAVECAC EXEC PGM=BPXBATCH,REGION=0M, // PARM='SH &J/java -Xshareclasses:cacheDir=&C,name=&N,&V' //STDERR DD SYSOUT=* //STDOUT DD SYSOUT=*
Strange behaviour
By using the startup option -verbose:class,dynload you can get information about the classes as they are loaded.
When not using shared classes, there were records saying <Loaded ….. and giving durations of the loads etc.
When using shared classes there were still a few instances of <Loaded… . I could not find out why some classes were read from disk , and the rest were read from the shared classes cache.
If we could fix these, then the startup would be even faster!
After some investigation I can explain some of the strange behaviour.
- When a jar is first used there is a <Loaded… for the class that requested the jar.
- A class like <Loaded sun/reflect/GeneratedMethodAccessor1 with a number at the end gets a <Loader… entry.
- Some other classes in a jar file get loaded using <Loader… though they do not look any different to classes which are loaded from the shared cache!
All in all, very strange.
Where do you harden the cache to?
By default the cache is saved to /tmp. As /tmp is often cleared at IPL, this means the cache will not exist across IPLs. You may wish to save it in an instance specific location such as /var/myprogram.
What happens if I change my Java program?
I had a small test program which I recompiled, and created the jar file. The Java source was
public class hw {
public static void main(String[] args) throws Exception {
System.out.println("This will be printed");
System.out.println("HELLo" ) ;
CPUtil.print(); // this prints Util.line 10
hw2.print();
}
}
When I reran the program the output contained
JVMSHRC169I Change detected in /u/adcd/hw.jar... ...marked 3 cached classes stale class load: sun/launcher/LauncherHelper$FXHelper from: .../lib/rt.jar <Loaded CPUtil> < Class size 427; ROM size 416; debug size 0> < Read time 4 usec; Load time 108 usec; Translate time 595 usec> class load: CPUtil from: file:/u/adcd/hw.jar Output from CPUtil.line 10 <Loaded hw2> < Class size 386; ROM size 368; debug size 0> < Read time 3 usec; Load time 107 usec; Translate time 635 usec> class load: hw2 from: file:/u/adcd/hw.jar
Where you can see output from my program is intermixed with the loader activity.
What happens internally
From the previous topic, it seems that Java has to read the files on disk for example to spot that a class has changed. This may just be a matter of reading the time stamp of the file on disk,or it may go into the file itself.
Should I use .class files or package the .class files into a .jar files?
This will be a hand waving type answer. Generally the answer is use a .jar file.
| Use one .jar file | Use multiple .class files |
| One directory access and one security access check should reduce the CPU usage. | Multiple directory access and multiple security checks are required. |
| Reading one large file may be faster than reading many smaller files. An I/O has “set-up I/O”, “transfer data”, “shutdown I/O” there is one set-up and one shutdown. | Each file I/O has set-up and shutdown time as well as the transfer time and is generally slower than processing bigger files. (Think about large block sizes for data sets). |
| The .jar files are compressed so there is less data to transfer. The decompression of the jar file takes CPU. | Files do not need to be decompressed |
| For integrity reasons you can have your .jar file cryptographically signed. | You cannot sign .class files. |
Should I use of BPXBATCH or BPXBATSL?
In the Tomcat script for starting the web server it issued
exec "/usr/lpp/java/J8.8_64/J8.0_64/bin/java" ... &
The & makes it run in the background. As I was running this as a started task, this seemed unnecessary and removed the &.
I also used EXEC PGM=BPXBATSL instead of EXEC PGM=BPXBATCH
The combination of both reduced the start time significantly!
I had to specify environment variable _BPX_SPAWN_SCRIPT=YES to be able to run the script. Without it I got
BPXM047I BPXBATCH FAILED BECAUSE SPAWN (BPX1SPN) OF … FAILED WITH RETURN CODE 00000082 REASON CODE 0B1B0C27
Problems I experienced while setting this up.
Group access
When restoring from a snapshot I used
java -Xshareclasses:cacheDir=/tmp,name=rseapi’,restoreFromSnapshot’, cacheDirPerm=0777,groupAccess’
Which worked.
When I omitted the group Access I had the following messages in stderr of my Java program.
JVMSHRC020E An error has occurred while opening semaphore JVMSHRC336E Port layer error code = -197358 JVMSHRC337E Platform error message: semget : EDC5111I Permission denied. JVMSHRC028E Permission Denied JVMSHRC670I Error recovery: attempting to use shared cache in readonly mode if the shared memory region exists, in response to "-Xshareclasses:nonfatal" option. JVMSHRC659E An error has occurred while opening shared memory JVMSHRC336E Port layer error code = -393966 JVMSHRC337E Platform error message: shmget : EDC5111I Permission denied. JVMSHRC028E Permission Denied JVMSHRC627I Recreation of shared memory control file is not allowed when running in read-only mode. JVMSHRC840E Failed to start up the shared cache. JVMSHRC686I Failed to startup shared class cache. Continue without using it as -Xshareclasses:nonfatal is specified c
The OMVS command ipcs -m gave
>ipcs -m IPC status as of Mon Aug 21 17:33:54 2023 Shared Memory: T ID KEY MODE OWNER GROUP m 8196 0x6100c70e --rw-rw---- OMVSKERN SYS1 m 8197 0x6100c30e --rw------- OMVSKERN STCGROUP
When the correct group access was specified the ipcs -m command gave
>ipcs -m
IPC status as of Mon Aug 21 17:38:40 2023
Shared Memory:
T ID KEY MODE OWNER GROUP
m 8196 0x6100c70e --rw-rw---- OMVSKERN SYS1
m 73733 0x6100c30e --rw-rw---- OMVSKERN STCGROUP
and the group mode has values -rw.
Wrong owner
I submitted a job to run Java which created the shared cache. I then tried running the same program using a started task with a different userid.
The cache on disk had access
-rw-rw---- 1 COLIN SYS1 32 Aug 25 11:05 C290M4F1A64_semaphore_zosmf_G41L00 -rw-rw---- 1 COLIN SYS1 40 Aug 25 11:05 C290M4F1A64_memory_zosmf_G41L00
But my started task was running with a different userid and group.
I got messages
JVMSHRC684E An error has occurred while opening semaphore. Control file could not be locked. JVMSHRC336E Port layer error code = -102 JVMSHRC337E Platform error message: EDC5111I Permission denied. (errno2=0xEF076015) JVMSHRC028E Permission Denied
I delete the cache entries, and restarted the started task. I also added another step to the started task to issue snapshotCache.
Assuming you are talking about SHRLIBRGN in the context of shared libraries, are you aware of WJSIGSHL? This gives a detailed list of what is currently in the cache.
I played with this around 15 years ago with a view of understanding how the caching behaved in a production environment, and after realising that almost all Java based products came with their own private version of java, the supposed benefit of being able to make effective use of a shared cache proved futile, especially as whatever got “in there first” after an IPL, was probably likely to be there for the life of the IPL (anything up to a year).
But if you have very simple environment with just a single version of java, then it may make sense, and may prove of some use to save I/O and real memory.
https://www.ibm.com/support/pages/system/files/inline-files/WP101320_-_SHRLIBRGNSIZE_and_Effect_on_31-Bit_JVM_Storage_Needs.pdf
LikeLike
Hi Graham,
Thanks for the info about WJSIGSHL- I’ll play with it and see if there is anything interesting.
Zowe has 4 started tasks, all running Java ( I think) so it looks like they could share java classes and shared libraries.
I see you can have different levels of Java shared classes – it would be good if the java that comes with z/OS has its shared libraries in its cache, then Zowe has stuff on top of it etc… I know using this stuff cut down my CPU of Z/OSMF by half! Zowe uses even more CPU, so it ripe for working with
Colin
LikeLike