When does JavaSE becomes a better choice than JavaME CDC?
A comment from my last entry on performance, asked, "I was thinking about the fact that devices [increasingly] get more power and more RAM. I thought when will JavaSE be a better choice instead of JavaME/CDC1.1? How much CPU, RAM, cache....do you need?"
Before I answer this, I must first make the disclaimer that my opinions are my own as an engineer, and not necessarily that of my employer, Sun, or even other engineers at Sun. With that said, now let's get into the question ...
JavaSE or JavaME?
While the initial question was motivated by devices capability and performance, there are many more dimensions to this question than first meets the eye. The issues also include compliance with specifications, ubiquity, portability, etc. Let's start with the obvious ...
If your device is like a desktop / server machine (think Pentium class) that is built into an embedded box, then, in general, the JavaSE VM can probably make better use of its capabilities for performance. If it's capabilities are a little less, then your mileage may vary. I say Pentium class here just to give you an idea of the type of computing capability involved. I don't mean only for a x86 type processor. Obviously, there are JavaSE ports for SPARC and PowerPC as well, and the same rule of thumb applies there.
Device capability isn't only about the choice of processors. Take PowerPC for example. It has embedded variants as well as the more well known desktop and server versions. The processor core is mostly the same (i.e. will execute the same code), but other capabilities are different. The most obvious would be differences in clock speed, and cache. And then, there are other hardware differences (e.g. board level) in capability: cache architecture, L2 cache, main RAM capacity, RAM speed, bus speed, memory and I/O bus architecture, I/O processors, MMU, DMA, TLAB size, secondary storage (HardDisks, FlashDisks), etc. ... or, the lack thereof. The more of these features your device has, the more likely JavaSE is a better fit, and vice versa.
Just looking at memory capacity alone, I think JavaSE typically operates with a footprint in the order of 10s to 100s of MBs, or even GBs. CVM operates in the order of 1s to the low 10s of MBs. Of course, a lot of this depends on what your application is doing (for both JavaSE and CVM). But those numbers should give you an idea. So, if your device only has 16MB RAM, CVM will probably be your best bet. If you have 32MB of RAM, it gets a little gray, and depends on what you are trying to achieve. CVM is still usually your best bet for most embedded applications. Low 100MBs, it is still gray but tending more towards JavaSE now. If the device has 1 GB or more, I would be fairly confident that JavaSE is better suited here.
As for cache, it's a lot harder to tell. 0 to 10s KBs, go with CVM. 10s to 100s of KBs, it's a gray area. MBs of cache, you can definitely run JavaSE now, but this doesn't mean that CVM isn't still the better choice in some cases.
For clock speeds, 10s to low 100s of MHz, CVM is your better choice. Low 100 MHzs to 1GHz, it's gray. More than 1GHz, JavaSE is the likely choice. But as Sun has shown not too long ago, CPU performance isn't all about clock speeds (see CoolThreads). So, take the above numbers with a grain of salt. In fact, all the ranges I've given above are just educated guesses based on my experience in this field. They can be used as a hints, but a real world case can be different. That's why there's no hard fast rule as to which fits better in any given case.
Now that we're talked about the obvious stuff, let's get into all the "gotchas" that people may not think about ...the Mind of an Embedded Systems Developer
Embedded hardware usually needs to have the following characteristics: cheap (cost-wise, not quality), reliable, and may lag the state-of-the-art technology curve by just a little ... not too much, but a little. It needs to be cheap because the target customer is the average person. The cheaper it is, the more you can sell. Reliability is obvious. No one wants an unreliable sytem. Plus, the experience of the blue screen of death (from a certain other computing experience) is totally unacceptable. In embedded devices, if the device fails, people can die or get seriously hurt ... or seriously annoyed, in which case, the device manufacturer's pocket book will get seriously hurt. Customers expect such systems to "just work". No exceptions. The lagging the curve part is mainly for cost reasons. Being on the leading edge of the curve is expensive. Lagging too much can also be expensive because parts (and their replacements) may be hard to find. Trailing just behind the curve a little typically yields the sweet spot. Obviously, this doesn't apply to every embedded device out there. But I'll focus on this perspective, and the exception cases will also fall out from this line of reasoning.
So, what does this mean to the embedded software (and JavaME) developer?
As the technology curve shifts, device hardware will tend to be upgraded to avoid incurring the cost of expensive rare parts based on old hardware. Of course, upgrading also has other benefits like more hardware capability. Sometimes, this upgrade is more than just going to the next revision of a CPU. Sometimes, it is a complete architecture change.
Sometimes, a manufacturer will also want to offer different grades of hardware with different capabilities e.g. low-end and high-end devices for 2 different market segments. These devices may not always be built on the same CPU architecture.
This is why JavaME is compelling (write once run anywhere). But WORA depends on the presence of a Java VM port. So, the portability of the VM is important. After all, there is development cost involved with the porting effort, and it makes sense that the manufacturer will want to minimize this. This is one area where CVM fits better. As I've explained in a prior article, CVM was architected to be easily ported to different platforms. This is not to say that JavaSE can't be ported, but it will not be as easy to do so. I have also pointed out previously that portability comes with a cost in performance. Though this cost is usually negligible in most cases, it is there. This is why CVM's performance in general will not match JavaSE on more capable devices, and conversely, why JavaSE will be harder to port. CVM chose portability. JavaSE chose performance.
By ubiquity, I'm not referring to ubiquity of Java enabled devices as is the traditional use of the term in this space. In this case, I am referring to the availability of VM ports. Ubiquity follows from ease of portability. Because CVM is easily portable, it is available in more ports (in terms of CPU architectures and OS platforms) than JavaSE today. For example, as of this writing, there are CVM ports for ARM, MIPS, x86, SPARC, and PowerPC. Some of our customers have CVM ports for other CPU architectures as well. As far as I know, JavaSE has ports for x86, SPARC, and PowerPC only. Your choice of which VM to use may be limited by what port source code exists for ... that is unless you intend to do your own port. While that is an option (and often sounds fun to engineers because of the hacking that you'd get to do), it usually takes time to get it done correctly. Hence, that needs to be factored against your development schedule. Also, even if you would choose to do a port, the fact that CVM is significantly easier to port means that you will have a higher probability of getting it done correctly sooner.
The fact that embedded systems are highly varied is why CVM is architected for portability. It is a necessary tradeoff.
Compliance with Specifications
Another factor you will need to consider is the compliance issue. Are there specifications which your device needs to comply with? For example, the Java platform that operates in set-top boxes and Blu-Ray Disc players is based on the Personal Basis Profile which sits on top of CDC. While it's possible to get PBP compliance on JavaSE's VM, it will take some work.
Fit for Purpose
Having said all the above, you should also make your decision based on what and how you are using the Java platform features in your device. Let's look at a few use cases:
Case 1: Your device is an x86 box that you're only manufacturing in limited quantity (say in the 10s or low 100s), and you can charge a lot of money for it. It comes with 1G of memory, and a hard disk storage. It primarily needs to run only one Java application. The application may be heavily threaded. Performance is critical. The application is special purposed and is sold only with the box. The box is not expected to run other applications, though you may send it upgrades to your application.
Chances are you will want JavaSE. The key: desktop/server class hardware, critical performance, special purposed software i.e. not subject to open standards compliance.
Case 2: Your device will be sold to millions. It runs an ARM processor. 32M RAM. Only on-chip cache. It is used in a vertical market with their own specification which has components based on JavaME CDC 1.1.
Chances are you will want CVM. The key: device needs to be cheap, minimal/low capability, needs CDC compliance.
Case 3: Your device is a server that needs to run JavaEE servlets ... about 1000 of them at the same time at any given moment. Each servlet needs to run in its own process, not thread. The servlet itself may be multi-threaded. The servlets are expected to be short lived, but there's just a lot of them. Performance is critical. Each servlet handles a service call coming in from the net. The services are headless (i.e. no graphics), and are not compute bound (i.e. no huge number crunching problems to solve). If the servlets are unresponsive enough, users of the device will be annoyed ... extremely annoyed. The device has 4GB of memory, and is configured with 4 super duper fast CPUs. Needs JavaEE compliance.
The obvious answer is ... JavaSE? Not necessarily. Here's why: JavaSE has a larger footprint than CVM. Let's say the base foot print of JavaSE is 20MB. The base foot print of CVM is under 2MB ... let's say it's 2MB. Let's say each servlet needs 4MB of memory to run. Hence, each instance of JavaSE running a servlet takes about 24MB of RAM. Each instance of CVM running a servlet takes about 6MB of RAM.
Since the device has 4GB of RAM, at any given time, it can hold about 170 instances of JavaSE, or it can hold about 682 instances of CVM ... that is if you don't want to deal with hard disk paging. Which one do you think will be more responsive to the 1000 service requests that is coming in at any given moment?
I'm not going to answer that. ;-) The answer is not as clear cut as you think. Obviously, just by looking at the device capability alone, you may not be making the best decision. On the other hand, the solution needs JavaEE compliance. CVM is fully compliant with the 2nd edition Java VM specification. Hence, you can run a JavaSE and JavaEE stack on top of it. But hey, wouldn't that increase CVM's foot print to more than just 2MB? Yes, it will. I don't actually have the VM footprint numbers for JavaSE. My example is contrived, but it may not be invalid. Regardless, it does illustrate my point that, sometimes, things aren't always as they seem. There are many factors to consider. Certainly, hardware / device capability isn't the only important factor.
What does this mean to you?
It is always good engineering practice to let your needs guide your decisions. Understand your needs (i.e. what your application needs to do), and then consider all the factors: device capability, portability, ubiquity, compliance, and maybe others that I've not thought of yet in the above. And then, pick what is best for your needs.
Have a nice day, and for those in the US, have a Happy Thanksgiving. :-)