Virtual Machines, Squeak, and Java

Download Report

Transcript Virtual Machines, Squeak, and Java

Virtual Machines:
Squeak and Java
Why use a virtual machine?
O-O, portability, memory, security
How Smalltalk compiles and executes
bytecodes
Specific details of Squeak’s VM
Object memory, garbage collection, primitives
How Java’s VM differs
Support for native types
7/16/2015
Copyright 2000, Georgia Tech
1
Why not just use C?
Strengths of Virtual Machines
Better for O-O
Objects are complex
VM handles initializations
Supports message sending
“Ask, don’t touch” Adele Goldberg
7/16/2015
Copyright 2000, Georgia Tech
2
Strengths of Virtual
Machines, part 2
Portability
Virtual machines allow for binary compatibility
between platforms
A simple VM implementation allows for easy porting
of the VM
Memory handling
Programmer doesn’t explicitly allocate and deallocate memory
Typically, no pointers
VM provides garbage collection
7/16/2015
Copyright 2000, Georgia Tech
3
Strengths of Virtual
Machines, part 3
Security
Not running on the bare machine, can be
safer
Explicit execution model
Easier to create reflective programs, metaprogramming
Threads and control structures are available
to programmer
7/16/2015
Copyright 2000, Georgia Tech
4
Strengths of Virtual
Machines, part 4
More memory efficient
Definitely an issue for low-power, lowmemory consumer devices
Can be faster startup
Side effect of more memory efficient
For some activities, speed is a wash
Floating-point, network...
7/16/2015
Copyright 2000, Georgia Tech
5
Compiling Smalltalk to
Bytecodes
Bytecodes are the instructions of the
Smalltalk virtual machine
The Smalltalk virtual machine is not
register-based (like Pentium, Sparc, etc.)
There are registers, but they’re not made
visible to the programmer
Instead, it’s stack-based
Parameters are pushed onto the stack, and
then are manipulated from there
7/16/2015
Copyright 2000, Georgia Tech
6
Kinds of Bytecodes in the
Smalltalk VM
 Push bytecodes
Put things (like instance variables) on the stack for manipulation
 Store bytecodes
From stack into object variable
 Send bytecodes
Causes a message to be sent
 Return bytecodes
Return top of stack, or nil, true, false, self
 Jump bytecodes
For optimizing branches
7/16/2015
Copyright 2000, Georgia Tech
7
Shhh…It’s not all messages!
Jump bytecodes allow us to optimize
some operations
7/16/2015
Copyright 2000, Georgia Tech
8
Example Compilation of Smalltalk
center
^origin + corner/2
 Compute the center point of a Rectangle
It’s actually a little different in current Squeak
But if you type the above in, it will compile to the same
bytecodes
 Bytecodes: 0, 1, 176, 119, 185, 124
7/16/2015
Copyright 2000, Georgia Tech
9
Meaning of bytecodes
 0: Push the value of self’s first instance variable (origin)
onto the stack
 1: Push the value of the receiver’s second instance
variable (corner) onto the stack
 176: Send + (result is left on the stack)
 119: Push the SmallInteger 2 onto the stack
 185: Send a binary message with the selector / (result is
left on the stack)
 124: Return the object on top of the stack as the value of
the message center
7/16/2015
Copyright 2000, Georgia Tech
10
Constants go in the Literal Frame
Each method gets compiled into a
CompiledMethod
A CompiledMethod includes a header, the
bytecodes, and a “literal frame”
The literal frame contains the constants from the
method
Strings, numbers that are too big to be SmallIntegers,
symbols for messages, etc.
Obviously, references to the literal frame are a bit
slower because it’s another memory access
7/16/2015
Copyright 2000, Georgia Tech
11
An Example with Literals
Transcript and the string are literals that
appear (earlier) in the CompiledMethod
7/16/2015
Copyright 2000, Georgia Tech
12
What does that mean?
Push on the stack the literal #Transcript
Duplicate it (for that cascade in a moment)
Push the constant ‘Hello World!’
Send the (literal) #show:
Pop the stack (we don’t care about the result)
Send the (literal) #cr
Pop stack
Return self
7/16/2015
Copyright 2000, Georgia Tech
13
Methods go in a MethodDictionary
Every method is compiled into a
CompiledMethod
The CompiledMethod is stored in a
MethodDictionary (one for every class)
with the selector of the method as the key
Lookup of a message involves finding the
class that can return a CompiledMethod
for the sought after message selector
7/16/2015
Copyright 2000, Georgia Tech
14
What the VM Interpreter Knows
The CompiledMethod being executed
The location of the next bytecode to
execute: Instruction pointer (IP)
The receiver (self) and arguments of the
message to this method
Any temporary variables
A stack
7/16/2015
Copyright 2000, Georgia Tech
15
What the VM interpreter does
Fetch a bytecode from the
CompiledMethod via the Instruction
Pointer (IP)
Increment the IP
Perform the function specified
7/16/2015
Copyright 2000, Georgia Tech
16
Contexts
When a message is sent, the active method
execution has to be saved
That’s saved as a context
Context is all the state of the VM interpreter
MethodContexts differ slightly from
BlockContexts
BlockContexts are created at runtime during
execution of a method
Optimizing contexts is a big part of making a VM
faster
7/16/2015
Copyright 2000, Georgia Tech
17
Primitives
Some CompiledMethods may just point to
primitives
Primitives are code inside the VM interpreter
for handling things like:
Arithmetic
Storage management
Control
Input-Output
7/16/2015
Copyright 2000, Georgia Tech
18
Object Memory
ObjectMemory allows manipulation of objects as
unique object pointers (oops)
Each oop is 32-bit
If bit 1 = 1, then the next 31 bits represent a
SmallInteger in 2’s-complement notation
If bit 1=0, then it’s an address to an object
Some Smalltalks make it an address to an ObjectTable
ObjectMemory also implements garbage
collection
7/16/2015
Copyright 2000, Georgia Tech
19
Object Header
Each object contains 1-3 bytes of header
information
3 bits for garbage collection
12 bits for hash value
5 bits for compact class index or 30 bits for
direct class oop
Size of object
7/16/2015
Copyright 2000, Georgia Tech
20
Garbage Collection
Many mechanisms exist for GC
Reference counting: Keep a count of all
references to an object. When it’s zero,
remove the object
Problems: Cycles
Every store to object memory requires
incrementing/decrementing (can defer with things
on stack)
7/16/2015
Copyright 2000, Georgia Tech
21
GC Part 2
Mark-sweep: Trace from a root object to
everything, marking what’s accessible in the
object header someplace
Sweep away everything else
Benefit: Memory gets compacted
Downside: When an object moves, can be hard to
fix it everywhere
• That’s why many Smalltalks use/used Object Tables
7/16/2015
Copyright 2000, Georgia Tech
22
GC, Part 3
Generation scavenging: Deal with objects in
generations. Move from eden to survivor
eventually to old or tenured
Based on observation that new objects tend to go
away quickly, while some objects stick around forever
Reduces amount of memory space to mark-sweep
By David Ungar, father of Self
(Fastest O-O language with all-object semantics)
7/16/2015
Copyright 2000, Georgia Tech
23
VM Optimizations in Squeak
Compact classes are cached for easy access
Smalltalk compactClassesArray
Methods are cached since they’re often re-used
Lookup of messages to methods are cached at
two-levels to prevent frequent message re-lookup
at: accesses an atCache first to speed Array
and similar references
7/16/2015
Copyright 2000, Georgia Tech
24
Garbage Collection in Squeak
Combination of generation scavenging and
mark-sweep
Two-generation scavenger
Normally, just does generation scavenging, but a full
gc also does mark-sweep
That’s why it takes two Smalltalk garbageCollect to
really clear out all of memory
On a 400 Mhz Powerbook, 3ms/second for
scavenger, 400 ms for full gc
7/16/2015
Copyright 2000, Georgia Tech
25
What if you want to interfere
with GC?
Can always fix it: ObjectMemory
Can also use WeakArrays
WeakArrays don’t prevent object reclamation
But if the last reference is from a WeakArray,
you can do something first
You appoint an executor and finalize
methods (see WeakRegistry)
7/16/2015
Copyright 2000, Georgia Tech
26
Exploring the Squeak VM
VM Interpreter is in Interpreter
Can actually run it to simulate Squeak
(InterpreterSimulator new openOn: Smalltalk
imageName) test
Object Memory is in ObjectMemory
The VM is generated using the
CCodeGenerator
Interpreter translate: 'interp.c' doInlining: true.
7/16/2015
Copyright 2000, Georgia Tech
27
Example: Implementation of
bytecodes
duplicateTopBytecode
self fetchNextBytecode.
self internalPush: self internalStackTop.
pushConstantMinusOneBytecode
self fetchNextBytecode.
self internalPush: ConstMinusOne.
7/16/2015
Copyright 2000, Georgia Tech
28
Making Your Own Primitives
You can use the CCodeGenerator to build
your own primitives
You can even use it to generate stubs that
you later rewrite with your own C
The key challenge is dealing with the
boxing and unboxing
Moving between native types and objects
There are issues of “glue”, conversions, etc.
7/16/2015
Copyright 2000, Georgia Tech
29
History of Primitives in Squeak
Primitives were just 1…256
Named primitives allowed for external shared
libraries (DLLs)
Named primitives can now be generated with
SLANG (System Language - minimal Smalltalk
that can be easily generated using code
generator)
Lots more on Squeak Swiki
7/16/2015
Copyright 2000, Georgia Tech
30
Making a Primitive, Step 1
For example: Build a primitive that returns
the sum of two input integers
Step 1: Define the plugin class
InterpreterPlugin subclass: #FooPlugin
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Werdna-Foostuff'
7/16/2015
Copyright 2000, Georgia Tech
31
Making a Primitive, Step 2
Step 2: Define the calling class and calling
method
Object subclass: #Foo
instanceVariableNames: 'myInteger '
classVariableNames: ''
poolDictionaries: ''
category: 'Werdna-Foostuff'
integerSum: firstInteger and: secondInteger
"answer the sum of firstInteger and secondInteger"
< primitive: 'primFooIntegerSumAnd' module: 'Foo'>
^FooPlugin doPrimitive: 'primFooIntegerSumAnd'
7/16/2015
Copyright 2000, Georgia Tech
32
What’s going on here
 < primitive: 'primitiveName' module: 'moduleName'>
 This will always fail until the primitive is defined, so
execution will fall through to the next line
 ^FooPlugin doPrimitive: 'primFooIntegerSumAnd’
 Which will allow us to test in Squeak
7/16/2015
Copyright 2000, Georgia Tech
33
Making a Primitive, Step 3
Step 3: Define the primitive method
primFooIntegerSumAnd ":firstInteger and: secondInteger"
"answer sum of (int) firstInteger and (int) secondInteger"
|firstInteger secondInteger|
self export: true. “Public function for extern use”
self inline: false. “Don’t bother inlining for speed”
secondInteger := interpreterProxy stackIntegerValue: 0.
firstInteger := interpreterProxy stackIntegerValue: 1.
interpreterProxy pop: 3.
interpreterProxy pushInteger: (firstInteger+secondInteger).
7/16/2015
Copyright 2000, Georgia Tech
34
What’s all that code?
It’s close enough to Smalltalk to execute!
inlining is compiling in the function rather than the
call to the function. Optimization technique.
InterpreterProxy fills in for the Squeak VM, which we
need to manipulate the context (e.g., push/pop
operations)
What we’re doing is:
Get the arguments
Pop them and the receiver object off the stack
Push back on the stack the result
7/16/2015
Copyright 2000, Georgia Tech
35
Making a Primitive, Step 4
Step 4: Test
Believe it or not, it works as-is!
The PlugIn code handles arguments and
other issues for you inside of Squeak
Foo new integerSum: 3 and: 4
7/16/2015
Copyright 2000, Georgia Tech
36
How did that work?
doPrimitive: primitiveName
| proxy plugin |
proxy := InterpreterProxy new.
proxy loadStackFrom: thisContext sender.
plugin := self simulatorClass new.
plugin setInterpreter: proxy.
plugin perform: primitiveName asSymbol.
^proxy stackValue: 0
7/16/2015
Copyright 2000, Georgia Tech
37
Making a Primitive, Step 5
Step 5: Tell the primitive its name and
compile to C
moduleName
"return the name of this plug-in library"
^'Foo'
FooPlugin translateDoInlining: true “Generate the C code”
7/16/2015
Copyright 2000, Georgia Tech
38
Making a Primitive, Step 6
Step 6: Compile your primitive!
You’ll need support files (e.g., .h files)
 InterpreterSupportCode writeMacSourceFiles
Generates them on the Mac
7/16/2015
Copyright 2000, Georgia Tech
39
What our primitive looks like in C
EXPORT(int) primFooIntegerSumAnd(void) {
int x; int y; int _return_value;
x = interpreterProxy->stackIntegerValue(1);
y = interpreterProxy->stackIntegerValue(0);
if (interpreterProxy->failed()) {
return null;}
_return_value = interpreterProxy->integerObjectOf((x + y));
if (interpreterProxy->failed()) {
return null;}
interpreterProxy->popthenPush(3, _return_value);
return null;}
7/16/2015
Copyright 2000, Georgia Tech
40
A Tour of the Java VM
While we’re getting our hands dirty with
bits, let’s talk about the Java VM
How does it differ?
What’s the same?
7/16/2015
Copyright 2000, Georgia Tech
41
Java VM also executes bytecodes
Like Squeak, Java VM is stack-based
Java VM bytecodes are typed
Java supports native data types, as well as objects
So we need integer push (iload) as well as float and
double (fload, dload)
But most of the rest are similar
Some special-purpose ones exist to optimize things
like switch
More Java bytecodes have operands
7/16/2015
Copyright 2000, Georgia Tech
42
Example of Java-to-bytecodes
void spin() {
int i;
for (i = 0; i < 100; i++) {
;
// Loop body is empty
}
}
7/16/2015
Copyright 2000, Georgia Tech
43
Bytecodes for Example
0
1
2
5
8
9
11
14
iconst_0
istore_1
goto 8
iinc 1 1
iload_1
bipush 100
if_icmplt 5
return
7/16/2015
// Push int constant 0
// Store into local variable 1 (i=0)
// First time through don't increment
// Increment local variable 1 by 1 (i++)
// Push local variable 1 (i)
// Push int constant 100
// Compare and loop if less than (i < 100)
// Return void when done
Copyright 2000, Georgia Tech
44
Java VM makes threads more
significant
In Java VM, stacks are associated with threads,
not with method contexts (frames) in Java
Frames are pushed and popped from the stack as
methods come and go
The same thread always keeps the same stack
There are bytecodes for monitorenter and
monitorexit for synchronized{} blocks
Every object has its own lock (semaphore)
7/16/2015
Copyright 2000, Georgia Tech
45
Java VM’s Exceptions
Exceptions are handled within the VM
When an exception occurs, current
method frame is searched for handler
If not there, pop the current frame, and
search the next one on the stack, until one
is found
Advantage: Fast
Disadvantage: Can’t continue
7/16/2015
Copyright 2000, Georgia Tech
46
Java Security Support
Java specifies very exactly a class file
format
That class file format must be followed for
the class to be executed
Java VM does some bytecode verification
The VM thus guarantees a level of
security
7/16/2015
Copyright 2000, Georgia Tech
47
Java’s Garbage Collection
Particular algorithm not specified in JVM spec
Exactly like Squeak
Java 1.1 used mark-sweep and object tables
Java HotSpot VM uses generational scavenging
and mark-sweep with direct pointers
Default: Two generations
Optionally: Three generations (“train”) for fewer gc pauses
Just like Squeak
7/16/2015
Copyright 2000, Georgia Tech
48
Java’s JIT (Just In Time
Compilation)
 JIT compilation turns a method into a native code
routine on-the-fly
 Native code routines are stored in a cache
 Hard to do well
Easy to implement different semantics
 Not portable
 Can give you outstanding speed benefits
But compilation itself takes time
So, on some benchmarks, raw VM is better than JIT
 Issue: Is it still a VM anymore? Do you still get VM
benefits?
7/16/2015
Copyright 2000, Georgia Tech
49
Comparing Squeak and Java VMs
Bytecodes
Surprisingly similar!
In fact, Java can be compiled to ST bytecodes
But not vice-versa easily
Java bytecodes are somewhat more complex
Typed, more operands
Garbage collection
Latest version of Java: Dead heat
7/16/2015
Copyright 2000, Georgia Tech
50
Squeak /Java VMs, Part 2
Exceptions
Squeak handles exceptions within Squeak
Java handles exceptions within VM
Java’s are faster
Squeaks are more flexibile (e.g., notification as an
exception that then continues)
Security
Definitely in Java’s favor
7/16/2015
Copyright 2000, Georgia Tech
51
Squeak/Java VMs, part 3
Threads/Processes
With Java, they’re all in the VM
Obviously, fast
More overhead in the VM
With Squeak, they’re done with Squeak and
primitives
Are they slower? Don’t know-anyone want to
measure?
7/16/2015
Copyright 2000, Georgia Tech
52
Conclusion: Squeak/Java VMs
Java
With JIT, faster
More secure, better support for threads
Much more complex and harder to port to different
platforms
Squeak
Slower
More flexible
Easier to port
7/16/2015
Copyright 2000, Georgia Tech
53
Summary
 VMs make a lot of sense still
VMs can ease programmer load, and make sense for modern
devices
 VMs have common architectural pieces
Bytecodes, stacks and contexts/frames, object memory
management
 Squeak VM can be extended with primitives, also written
in Squeak
 Java’s VM is more like Squeak’s VM than unlike it
But faster, more secure, and more complex
7/16/2015
Copyright 2000, Georgia Tech
54