SAN FRANCISCO (06/29/2000) - JavaOne Today: What have you been working on recently here at Sun Microsystems Inc.'s Labs?
Gosling: These days, I'm putting together a group to work on developer tools based on work done by the NetBeans folks, who put together this really nice framework for building tools.
One of the things I'm trying to do is to build a set of tools that professional developers would use. Typically, tool vendors tend to go after the mass-market developer, and I guess I'm not one of them, so they generally don't interest me. And, by and large, all the tool developers have kind of ignored the higher-end developers. So I'm spending some time thinking about that.
But, in actual fact, I've spent most of the last year working on the realtime spec for Java. That's really what's occupied me for most of the last year. I was doing that pretty much full-time until three or four weeks ago, when we finally got a camera-ready copy of the book off to Addison-Wesley.
JavaOne Today: Which book?
Gosling: The realtime specification for Java. It's a spec that was done under the Java Community Process.
JavaOne Today: Could you give an overview of what's in that spec and what it's all about?
Gosling: The realtime spec for Java is all about how you write programs in Java on realtime systems. And there are a number of components to it -- simple things like dealing with accurate dates, scheduling events. Most of the hard things have to do with deterministic timing.
In the realtime world, what people care about is performance, to a certain extent. But more important than performance is, generally, determinism.
When you say, "I want this to be executed at that time," in general, by God, you mean it. And then it's not allowed to slip. And that has effects in two areas. One is scheduling, and there's all kinds of exotic scheduling algorithms that can be in there, and the realtime spec has a general framework for putting in all kinds of schedulers.
The other side of it is that there's one big source of non-determinism in timing in Java right now, and that's garbage collection. And garbage collection is really the number one hard problem.
On the one hand, people look at garbage collection and what it gives you, and they by and large really like it. Particularly folks in the realtime world like garbage collection a lot because it's tremendously useful for increasing the reliability of systems and speeding up development time. It just helps to hold the whole robustness story tremendously.
JavaOne Today: You said particularly the realtime people like that.
Gosling: Yeah. Well, because they tend to be even more worried about systems that crash. With a lot of these realtime systems, if the software crashes, an actual person dies. It's a control system for an airplane or a robot arm -- you're just getting ready to stop the arm and you garbage collect and the arm just keeps going because nobody's telling it to stop. And it takes out a wall or innocent passers-by or something. And so you can't tolerate that kind of thing.
And so there was a general consensus that we wanted to have garbage collection in there but we didn't want to live with the hiccups that you get when your average garbage collector kicks in. All garbage collectors have the effect that they tend to shut down the system for some interval of time. And some of them you can actually tune the interval.
So, like in HotSpot, there's a limited amount of control that's possible over how long each GC step takes. But if you're writing an application whose timeliness needs to be tighter than what something like the HotSpot VM can do, then you're in trouble.
One of the things that we decided fairly early on was that an easy out on this problem would be to, say, only use "realtime garbage collectors." That's garbage collectors that have essentially zero pause time because they either back out or are doing things truly in parallel.
A couple of people in the group did a literature survey and they concluded that real realtime garbage collectors essentially didn't exist. There were a lot of things which were claiming to be realtime garbage collectors that were close but not good enough. And the usual extent of closeness was that the people were saying, "Well, we're realtime because we never pause more than a millisecond."
Or, "We're realtime because only one chance in a billion will we ever actually back off and do a full mark-and-sweep."
Unfortunately, both of those invalidate the GC algorithm in some applications.
In the realtime world, people are worried about the worst case. They don't care at all about the average case or the best case, they care about the worst case.
And if the worst case is that this algorithm will actually do a full mark-and-sweep -- shut the thing down for seconds -- then it doesn't work, even if it happens once in 10 or 15 days of running. If the thing that's running is the thing that's controlling the flutter of the wingtip of an F-16, after 10 days the airplane turns into dust. And that doesn't work.
For GC algorithms that have fairly small minimum times, often people will claim that they have a realtime algorithm, and then they'll say it's got a maximum pause time of a millisecond. And that works for some applications and doesn't for others. One way to characterize realtime algorithms is based on the maximum latency that they can tolerate. And some of them can live just fine with a millisecond; some need something much finer than that.
So what we ended up doing was defining a way to allow a special kind of thread to run even while the garbage collector is running. And if you know much about garbage collectors then you think about what is it that has to be true about a thread if it's going to run while the garbage collector is running. You end up with a fairly onerous set of restrictions. Namely that whatever the thread does, it does not allow it to touch an object in the garbage collector heap, and it's not allowed to move a pointer to one of these objects around.
And so we've defined two subclasses of thread, one called the realtime thread, which is a thread that has all kinds of extra scheduling parameters besides just the priority. And then there's another subclass of that called the no-heap realtime thread, which is one that also has the ability to run while the garbage collector is running. But those threads are not allowed to access heap memory. And the way that they are able to work is that there's a way to allocate what are called "immortal objects." An immortal object is one that is allocated -- it stays there forever and it never, ever goes away.
The immortal objects correspond pretty much to what people actually do as standard practice in realtime coding today. Generally when people are doing serious realtime work they pre-allocate everything. And then they sort of go into their realtime reading windows with the things which realtime never, ever allocates. Because it turns out that almost all the allocators in the world actually have non-deterministic timing.
See, almost everybody's malloc() or free routine has non-deterministic timing.
And so in general, people don't use them for the intense realtime application.
So the whole no-heap thing/immortal objects, if you describe that to an average Java programmer, they'd say, "Oh my God, this is really awful." But if you describe it to somebody who's been doing realtime programming, their reaction is sort of, "Gee, that's what I do today. It's not a big problem."
But it gives them the advantage of this universe where people doing these really time-critical things can actually interact with the other world that is done in Java, and get all the advantages of writing programs in Java, and also get all the portability stuff. That's really the most important piece.
JavaOne Today: How do the new scheduling capabilities work?
Gosling: With scheduling, there are some new objects introduced and one is called the scheduler. And a scheduler determines when threads should get started up. And one of the parameters of the scheduler is if it's a no-heap thread, it can then start while the garbage collector is running.
But normally, the scheduler is similar to what happened in typical thread scheduler. It's looking at the queues of threads that are available to be one.
They picked one, and in most everyday computer systems these fairly simple priority-based schemes work pretty well.
In realtime systems, generally you've got a notion of a deadline when this particular computation has to be done. And so there are ways to associate properties with threads like what its deadline is, what the expected amount of work is. And that gives a scheduler more information to sort of trade off which things to run now.
It's an open-ended framework, so that people can invent new schedulers and plug them in. There's also hooks in there for doing what people in the realtime world call feasibility analysis. Namely, you've got a set of threads, and you ask the question, "So does this make any sense?" If you've got three or four threads, each of which wants 100 percent of the CPU all the time, it makes no sense at all because you'd need three or four CPUs. Or three or four times as much CPU as you've got.
So you can do things like back out or not start systems, or go into some sort of fail-safe mode.
JavaOne Today: What do you mean by "back out"?
Gosling: Like not start the system. It's application dependent. When the feasibility analysis fails, what do you do?
JavaOne Today: So the VM does the feasibility analysis before it starts? Or does the application do it?
Gosling: The application effectively is in charge of invoking it. Because what the application does is it sets up a set of threads. And then it asks the scheduler "Is this feasible?" And if it is, then the system carries on. If it's not, then the application decides what to do. And it may do things like decide to cut back on its workload somehow. Or it may just even refuse to start.
It's sort of like having the "on" button on your airplane, if it turns it on and says, "Sorry, install a new CPU."
JavaOne Today: Is this going to be incorporated into all future VMs? Or will there be realtime VMs and a not-necessarily-realtime VMs?
Gosling: Yeah, it really ends up being two. There's the Java VM and then there's sort of a specialized class of Java VMs which would be the realtime Java VMs.
JavaOne Today: So there are certain kinds of applications then that would run on realtime VMs that just wouldn't work on regular VMs?
Gosling: Correct. The standard VM is not optimizing for the realtime performance. In general, what non-realtime systems are optimizing for is not timeliness but throughput. So you're trying to see how much raw performance you can get out of the whole system. And whether you're hitting your deadlines on a millisecond by millisecond basis isn't really relevant.
JavaOne Today: Why don't we switch gears and talk a bit about mobile code. I've had a few concerns about the practicality of sending objects across the network, and I would be curious to hear what your thoughts are. Let's start with finalization.
One of the issues that has come up in Jini discussions is cleaning up after mobile objects. If I'm a client and I receive a mobile Jini service object, that object can consume my local resources by firing off a thread, opening a socket, and so on.
From a Jini lookup service, I can grab service objects about whose interface I have absolutely no prior knowledge. I can grab them by name. I can use the browser methods to find things that look interesting and grab them. If I have prior knowledge of the interface of the object, then there's probably a cleanup method that I'm supposed to call when I'm done with it. If the object is nice, it will relinquish all of my resources that it consumed when I invoke the cleanup method.
JavaOne Today: But first of all, it may not be nice. And second of all, I may not have any prior knowledge of that interface. So how do we make sure that clients don't always run out of resources when they're using mobile code?
Gosling: There are several answers to that. One is when you stop referencing that object, it goes away. And things that it refers to go away. And the finalizer will get called.
In other words, that it's actually pretty rare to bring in some mobile object, not have some idea what interface it implements. Generally, these pieces of mobile code are used in things like editor plug-ins or database plug-ins, and they tend to do a Class.forName() or some sort of look-up that gives you this new Class object. And you do a newInstance() on it. And then you say, well gee, are you a plug-in for this area? Are you a device manager? Or are you a printer device manager? Then you can start talking to it.
If you have absolutely no idea what you're expecting, just take an arbitrary object in, there's not a whole lot you can do with it other than say, "Hey, isn't this nice?" And almost always, people do have standardized interfaces for these things.
And if you're designing one of these standardized interfaces and having the imported stuff go through some active and passive phases where you're getting rid of, dumping resources and that, then defining a method to clean things up is a good thing to do.
JavaOne Today: There's also denial-of-service attacks or bugs where even if you do call the cleanup method, if the method forgets to kill a thread, that thread doesn't go away.
Gosling: Sure. Denial-of-service attacks are generic and are really hard to deal with. And unfortunately, they're hard to deal with in a relatively deep sense. You can throw some signal at a thread. The stop() methods are there.
They're deprecated. The manual says, "Don't use this. Bad things will happen."
But they are there, right? And so you can stop a thread.
And all the VMs I know of, stop() actually does work. And unfortunately it does have some side effects, it has some consequences. But it's like the consequences that exist in any other system when you try to forcibly shut down something without giving it a chance to clean up. It certainly happens with Unix processes, with threads, anybody. C library, if you just shoot a bullet into its head, it doesn't clean up.
And if there were things that needed cleaning up, like reconstructing some data structure, like this thing representing a filesystem that's linked into some linked list, you could leave that kind of broken.
It absolutely happens with everybody's systems. Whether it's C threads or Unix processes, you have exactly the same problems. It's essentially impossible to stop a process and guarantee that it will be clean, that it will be graceful.
All the deprecation of stop says is you can't guarantee that it's graceful.
And it's not any different than what happens with stop in any other system.
It's just that we decided that it was important to spell out to people that this is a dangerous thing to do, and what you really ought to do is do something more graceful.
Where you really get into trouble is where people start using things like stop as a standard way of shutting down a thread. And really, the right way to do it is you have some application-specific way of doing it. The cleanest ways are, generally, if you've got like a thread that's in a loop, doing something, then you put a global variable in there that says, "I want it shut down." Have the thread actually testing it. And then at a point when everything is clean, you exit the loop.
Then, if you have your own cleanup method, you call a cleanup method. And unfortunately, graceful shutdowns, because they are dependent on exactly what the semantics of the operation are, they're intrinsically application-dependent.
So the right thing to do is to do the layered approach to shutting down some thread or some object. Namely, you tried to do it gracefully. If a graceful shutdown doesn't work, then depending on the situation you either pull out a gun and you either shoot that thread, or you shoot the entire system.
JavaOne Today: I thought you were going to say shoot the programmer who sent you that code.
Gosling: Well, right. You kind of pick up your pistol and you decide where to aim it at.
JavaOne Today: Another concern I had about Jini was testing, because the trend is over time, everything will become network-connected embedded devices. And we'll want all those devices to work together. In a world where everything is connected to everything, Jini kind of offers a way for things to work together at that level of interfaces instead of protocols.
The concern I have is that let's say I have a blender. I want my blender to work with all the clients that know about the well-known blender interface. I can't test my blender with all the clients, because there are too many of them, and I don't know what they are, and a lot of them don't exist yet. My service object is going to fly into these clients and is supposed to work with them.
So what has to happen is we have to agree on these semantics, the APIs.
Gosling: Right. The API is a contract. And by God, you better follow it. And by God, you better write it down because your point about some of these clients haven't been invented yet, that's absolutely true. And that's the way you really want the universe to be. That you can define a blender that's going to be controlled by or interface to things that haven't even been invented yet.
Because if you have a universe where in order to work, you have to test that blender against everything that interfaces to it, then you do that testing, and then never again can anybody invent something that interfaces to a blender.
And that's kind of a way that things like Windows ended up getting stopped. And a lot of that has to do with things like, what is the API to Windows? And the answer is, nobody knows. In fact, even Microsoft doesn't know what the Windows API is. Because they publish an API, but lots of people sneak in through back doors. And so you go to any bookstore and you can find these books:
"Undocumented Windows Secrets."
And those books are usually of the form, "If you get this handle back from so and so, then you cast it to an integer and you add 13, you'll find in bit three something that tells you this." Right? And of course the people who built the system, yeah, they might have that bit there, but that is a part of their internal implementation. They have no idea that people on the outside were using this.
So they know what pieces of the interface they specify to people on the outside. But because there isn't a really good, strong interface notion in C, people just sort of reach in through the back doors. And they do God knows what. And so when Microsoft Corp. goes to release a new version of Windows, what they essentially have to do is test everything. They take, I don't know how many thousands of applications that they run it against, they run them against Windows. And some of the ones that are the worst in terms of running through the back doors are the games.
The games seem to be just completely savage about doing whatever it takes to find whatever display chip you've got. Tweaking with the guts of the chips registers in parallel with the device driver. And that's kind of standard practice in the gaming world.
JavaOne Today: They probably see themselves as programming the hardware.
Gosling: Yeah, it gives them that performance, but holy mackerel, does it make life hard.
JavaOne Today: What you're saying is because these APIs would be Java, they're object-oriented, you can make the implementation inaccessible to people from the outside. So they can't get that integer and tweak -- add 13 and tweak this bit, because it's part of the implementation.
Gosling: Right. It was a very important property of interfaces that they are very strict. That wasn't just me deciding to be nasty. That was: the world is madness if the contracts aren't strict. Because then all of a sudden -- the whole notion of object-oriented programming falls apart if the interfaces aren't strict.
Because then all of a sudden you lose the ability to unplug this and plug that in. Because if you don't know what the shape of the plug is, how do you know it can plug in? And whether plugging in on this side or plugging in on that side, you just don't know.
JavaOne Today: I still have a question, though, about semantics. First of all, Java will keep implementation accessible to the client side -- let's say a Jini service or whatever. But I still have to understand that spec, which is written in a human language, and the JavaDoc comments that were also written in a human language. And people interpret human language descriptions differently.
Gosling: Absolutely. The whole issue of doing semantic specifications is actually very hard. Lots of people have taken many runs at it. You've got languages like Eiffel, which have pre-conditions and post-conditions in that.
And that gives you a piece of it. And you can do some of that by hand in Java, and actually the assertion facility is intended to be a part of that.
In terms of going far enough that you could actually not have an English specification, but only have essentially a machine-understandable specification that forms a contract, that's been a Holy Grail for generations of mathematicians. And the answer ends up being essentially that it's been pretty much a failure. Because the specifications are themselves a source of error.
JavaOne Today: You mean the specification in the formal language?
Gosling: Right. When you go from a language to some other language -- whether it's a formal language, some sort of mathematical language -- you still have the same problems. Namely that the formal specification may itself be ambiguous. It might not have occurred to the guy who wrote it down that thus and such a problem could be there.
And so you get exactly the same problems. And on average, while formal specifications help, they don't solve the problem. They just move it around.
JavaOne Today:Java has four access levels. The default is package. I have always wondered if making package access default was convenient because the three keywords that people from C++ already knew about were private, protected, and public. Or if you had some particular reason that you felt package access should be the default.
Gosling: A package is generally a set of things that are kind of written together. So generically I could have done one of two things. One was force you always to put in a keyword that gives you the domain. Or I could have had a default value. And then the question is, what makes a sensible default? And I tend to go for what is the least dangerous thing.
So public would have been a really bad thing to make the default. Private would probably have been a bad thing to make a default, if only because people actually don't write private methods that often. And same thing with protected.
And in looking at a bunch of code that I had, I decided that the most common thing that was reasonably safe was in the package. And C++ didn't have a keyword for that, because they didn't have a notion of packages.
But I liked it rather than the friends notion, because with friends you kind of have to enumerate who all of your friends are, and so if you add a new class to a package, then you generally end up having to go to all of the classes in that package and update their friends, which I had always found to be a complete pain in the butt.
But the friends list itself causes sort of a versioning problem. And so there was this notion of a friendly class. And the nice thing that I was making that the default -- I'll solve the problem so what should the keyword be?
For a while there actually was a friendly keyword. But because all the others start with "P," it was "phriendly" with a "PH." But that was only in there for maybe a day.
JavaOne Today: Well, I think one consequence of having package access be the default is that a lot of data ends up package access. I myself like to make data private, but I sometimes forget to type "private" so the data ends up package access.
Gosling: Yeah, and it sort of gets into another thing -- one of the things that I had wanted to do early on was formalize setting and getting something in the language. You know how in JavaBeans there's this sort of convention that you write setter and getter methods? But I did a bunch of surveys of developers at the time, about whether or not they would like this to be there. And the average person went, "Oh my God!"
And so I didn't do it. But I think in retrospect, I should never have listened to them. I should have just done it. Because, I mean Beans basically layered on this facility that I had wanted to do anyway, but Beans did it as kind of an afterthought.
And because it's layered on as a naming convention, there's some things that don't fit together real well. And so, for instance, it would've made a lot of sense for the default protection for an instance variable to be private. And then the getters and setters, which the system would've known about at a much deeper level, to make them either public or package.
JavaOne Today: About four years ago, I read an article where you described a Doobie Brothers concert that you went to. You said you were kind of looking up at the lights flashing and you saw packets flying across the wire, and I assume that's mobile objects. Could you clarify what that story was about?
Gosling: We were having this debate about building these systems, and to what scale people would go. You know, would people go all the way down to the light bulb?
And I was sitting there on this Doobie Brothers concert sort of staring up. And they had all these robotic searchlights -- spotlights -- there are these MIDI-controlled lights that a lot of these folks use. And I could see that all the network wiring and all of these spotlights were robotically pointing at the appropriate place.
And I was looking up there and said, "Light bulbs on a network. This is serious."
About the author
Bill Venners has been writing software professionally for 14 years. Based in Silicon Valley, he provides software consulting and training services at Artima Software Inc. He is the author of Inside the Java 2 Virtual Machine (Enterprise Computing, October 1999) and the creator of artima.com, a resource for Java and Jini developers.