Grundlagen
Speicher
- Hauptspeicher (main memory): Speicherort für Informationen in einem Computer
- Anderer Name RAM (random access memory)
- Information wird in Form von Bytes gespeichert (1 Byte = 8 Bit)
00000000–11111111 bzw. 0x00–0xFF bzw. 0–255 - Byte ist die kleinste Einheit, die im Hauptspeicher ansprechbar ist
- Zwei gängige Technologien für RAM sind SRAM und DRAM
Der Arbeitsspeicher oder Hauptspeicher (engl.: main memory, main store, primary memory) [… ist die] Bezeichnung für den Speicher, der die gerade auszuführenden Programme oder Programmteile und die [dabei] benötigten Daten enthält. Der Hauptspeicher ist eine Komponente der Zentraleinheit. Da der Prozessor unmittelbar auf den Hauptspeicher zugreift, beeinflussen dessen Leistungsfähigkeit und Größe in wesentlichem Maße die Leistungsfähigkeit der gesamten Rechenanlage.
Quelle: Duden Informatik
Einheiten (alte Form)
Missbräuchliche Verwendung der SI-Prefixe für Zweierpotenzen
- Kilobyte (kB) = 210 Byte = 1024 Byte
- Megabyte (MB) = 220 Byte = 1024 kB
- Gigabyte (GB) = 230 Byte = 1024 MB
- Terabyte (TB) = 240 Byte = 1024 GB
- Petabyte (PB) = 250 Byte = 1024 TB
- Exabyte (EB) = 260 Byte = 1024 PB
Die Prefixe des SI-Systems sind für Zehnerpotenzen reserviert. Da es aber bis zur Mitte der 1990er Jahre keine Prefixe für Zweierpotenzen gab, wurden häufig die SI-Prefixe „missbraucht“. Dies hat leider zur Folge, dass nicht klar ist, was mit MB gemeint ist. Gerade die Hersteller von Festplatten interpretieren MB als 106 Byte, da sie dann die Kapazität höher angeben können.
Einheiten (neue Form)
Einheiten nach EC 80000-13:2008 für dezimale Einheiten
- Kilobyte (kB) = 103 Byte = 1000 Byte
- Megabyte (MB) = 106 Byte = 1000 kB
- Gigabyte (GB) = 109 Byte = 1000 MB
- Terabyte (TB) = 1012 Byte = 1000 GB
- Petabyte (PB) = 1015 Byte = 1000 TB
- Exabyte (EB) = 1018 Byte = 1000 PB
Einheiten nach EC 80000-13:2008 für Zweierpotenzen
- Kibibyte (KiB) = 210 Byte = 1024 Byte
- Mebibyte (MiB) = 220 Byte = 1024 KiB
- Gibibyte (GiB) = 230 Byte = 1024 MiB
- Tebibyte (TiB) = 240 Byte = 1024 GiB
- Pebibyte (PiB) = 250 Byte = 1024 TiB
- Exbibyte (EiB) = 260 Byte = 1024 PiB
Die neuen Einheiten nach EC 80000-13:2008 unterscheiden sauber zwischen den dezimalen Prefixen und denen für Zweierpotenzen. Die neuen Einheiten haben sich aber nicht flächendeckend durchgesetzt, sodass immer noch nicht klar ist, welche Einheit jemand meint, wenn er von Megabyte oder Gigabyte spricht.
SRAM
SRAM (static random-access memory)
- Flipflop speichert ein einzelnes Bit
- sehr schnell aber teuer
- braucht keinen Refresh
Static random-access memory (deutsch: statisches RAM, Abkürzung: SRAM) bezeichnet einen elektronischen Speicherbaustein. Sein Inhalt ist flüchtig, das heißt, die gespeicherte Information geht bei Abschaltung der Betriebsspannung verloren. Im Gegensatz zu dynamischem Speicher (DRAM), welcher zur Vermeidung von Datenverlust ein periodisches Auffrischen (engl. refresh) benötigt, kann der Dateninhalt im statischen RAM bei Anliegen der Betriebsspannung beliebig lange gespeichert werden, wovon sich die Bezeichnung dieses Speichertyps ableitet.
Die Informationen werden durch Zustandsänderung von einer bistabilen Kippstufe in Form eines Flipflops pro Bit gespeichert. Das erlaubt es zwar, die Speicherzelle schnell auszulesen, aber im Vergleich zu dynamischen Speicherzellen ist die Speicherzelle verhältnismäßig groß. Im statischen Betrieb (Halten der Information) ist der Leistungsbedarf einer Zelle sehr klein.
Quelle: Wikipedia
DRAM
DRAM (dynamic random-access memory)
- Kondensator speichert ein einzelnes Bit
- langsamer als SRAM aber deutlich billiger (höhere Packungsdichte)
- Speicher muss regelmäßig aufgefrischt werden, da Kondensator Ladung verliert
Dynamic Random Access Memory (DRAM) bezeichnet eine Technologie für einen elektronischen Speicherbaustein mit wahlfreiem Zugriff (Random Access Memory, RAM), der hauptsächlich in Computern eingesetzt wird. Das speichernde Element ist dabei ein Kondensator, der entweder geladen oder entladen ist. Über einen Schalttransistor wird er zugänglich und entweder ausgelesen oder mit neuem Inhalt beschrieben. Der Speicherinhalt ist flüchtig (volatil), das heißt die gespeicherte Information geht bei fehlender Betriebsspannung oder zu später Wiederauffrischung verloren.
Ein Kennzeichen des DRAM ist die Kombination aus einer sehr hohen Datendichte auf einer kleinen Chipfläche, verbunden mit sehr preiswerten Herstellungskosten. Diesem Vorteil des DRAM gegenüber dem SRAM steht der Nachteil gegenüber, dass sich die im Kondensator gespeicherte Ladung und damit die gespeicherte Information aufgrund von Leckströmen mit der Zeit verflüchtigt, wenn sie nicht periodisch wieder aufgefrischt wird. Dies ist normalerweise in Abständen von einigen Millisekunden erforderlich. Das Auffrischen des DRAMs verbraucht außerdem im Ruhezustand eine gewisse Menge von Energie. Deshalb bevorzugt man in Anwendungen, bei denen es auf geringen Ruhestrom ankommt, SRAM.
Quelle: Wikipedia
Über die Select-Leitung (blau) wird die Zelle ausgewählt, über die Bit-Leitung (grün) werden die Daten aus der Zelle gelesen bzw. in die Zelle geschrieben.
Vergleich DRAM / SRAM
Man erkennt deutlich den Größenunterschied zwischen den beiden Arten von Speicherzellen. Während eine DRAM-Zelle nur einen einzigen Transistor benötigt, müssen in einer SRAM-Zelle sechs Transistoren verbaut werden. Wegen des drastischen Größenunterschiedes werden SRAM-Zellen nur dort verwendet, wo eine sehr hohe Performance nötigt ist, also vor allem für die Register des Prozessors und die Caches im Prozessor selbst.
DRAM-für ein Byte
Durch das Zusammenschalten von acht DRAM-Zellen kann man acht Bit und damit ein Byte speichern. In allen modernen Computern ist der Hauptspeicher nicht Bit-, sondern Byte-weise organisiert, d. h. es werden immer acht Bit gleichzeitig angesprochen. Eine Lese- oder Schreiboperation im Hauptspeicher adressiert immer mindestens ein Byte.
Das die Zellen für ein Byte immer gemeinsam adressiert werden erkennt man daran, dass sie sich eine Select-Leitung (blau) teilen.
Speichergrid
Der Hauptspeicher eines Computers besteht aus sehr vielen Zellen, die jeweils ein Byte speichern.
Adresse
Um die einzelnen Bytes im Hauptspeicher unterscheiden zu können, muss man ihnen eine eindeutige Adresse (address) geben. Hierzu werdend die Speicherstellen beginnend bei 1 durchnummeriert. Die Adresse ist somit selbst wieder nur eine Zahl, die eine Speicherstelle im Hauptspeicher eindeutig identifiziert. Wie alle Zahlen, mit denen ein Computer agiert, hat die Adresse eine feste Breite.
Bitbreiten
Eine wichtige Eigenschaft einer Computerhardware ist, wie lang die Adressen maximal werden können. Übliche Größen sind hier 16-Bit, 32-Bit und 64-Bit. Für PCs und Server ist heutzutage 64-Bit die übliche Größe, 32-Bit finden sich noch häufig bei Smartphones und 16-Bit trifft man bei Mikrocontrollern an.
Aus der Länge der Adresse ergibt sich, wie viel Speicher der Computer maximal verwalten kann.
- 16-Bit: 216 Byte = 64 kByte
- 32-Bit: 232 Byte = 4 GB
- 64-Bit: 264 Byte = 16.777.216 TB
Speicherung eines Bytes
Wenn man ein Byte in den Speicherzellen des Hauptspeichers ablegt, stellt sich die Frage, wie man die einzelnen Bits anordnet. An einer Seite befindet sich das Bit mit der geringsten Wertigkeit, das LSB oder least significant bit, auf der anderen Seite das MSB bzw. most significant bit. Entsprechend bezeichnet man die beiden Seiten als high-order end und low-order end.
Größere Einheiten
- Wort (word): 2 Byte, 16 Bit
- Doppelwort (double word, dword): 4 Byte, 32 Bit
- Langwort (long word): 8 Byte, 64 Bit
Viele Datentypen (siehe unten) benötigen mehr Platz, als ihn ein einziges Byte bietet. Daher haben sich für bestimmte Breiten (2, 4 und 8 Byte) eigene Begriffe etabliert.
Endianess
- Für Worte und längere Datentypen stellt sich die Frage, wie man sie im Speicher ablegt
- Big Endian: Das höchstwertige Byte wird zuerst geschrieben (an niedriger Adresse)
- Little Endian: Das niederwertige Byte wird zuerst geschrieben (an niedriger Adresse)
Wenn Einheiten gespeichert werden, die größer als ein Byte sind (z. B. ein Wort), stellt sich sofort die Frage, an welcher Adresse die Bytes gespeichert werden, aus denen die Einheit besteht. Analog zu den Bits gibt es auch hier ein low-order byte und ein high-order byte. Leider gibt es bei den Computerherstellern keinen einheitlichen Standard, sodass es sowohl Computersysteme gibt, die das high-order byte an der niedrigeren Adresse speichern (Big Endian z. B. bei IBM PowerPC) und solche, die das high-order byte an der höheren Adresse speichern (Litle Endian z. B. bei Intel).
Entwicklung eines Java-Programms
Da es sich bei Java um eine Hochsprache handelt, die compiliert werden muss, besteht die Entwicklung eines Java-Programms immer aus mindestens drei Schritten:
- Editieren (edit): Eingabe des Programms im Editor/in IDE
⇒ Quelltext (source code)
- Compilieren (compile): Übersetzen des Quelltexts durch den Compiler (
javac)
⇒ Java-Bytecode - Ausführen (run): Ausführen des Programms auf der Java-VM (
java)
⇒ Laufender Java-Prozess und Ausgabe
1. Editieren
Editieren (edit): Der Quellcode (source code) des Java-Programms wird mit einem Texteditor oder einer Entwicklungsumgebung (z. B. IntelliJ, Eclipse) geschrieben.
- Eingabe: Programmierer:in schreibt Java-Code in einer Datei mit der Endung
.java(z. B.HalloWelt.java) - Ergebnis:: Die Java-Quelldatei (
HalloWelt.java)
2. Compilieren
Compilieren (compile): Der Java-Compiler (javac) übersetzt den Quellcode in Bytecode – eine Zwischenform, die von der JVM verstanden wird.
- Eingabe: Java-Quelldatei (
HalloWelt.java) - Ergebnis: Eine Bytecode-Datei mit der Endung
.class(z. B.HalloWelt.class)
3. Ausführen
Ausführen (execute): Die Java Virtual Machine (JVM) führt den Bytecode aus.
- Eingabe: Die
.class-Datei (z. B.HalloWelt.class) - Ergebnis: Programm läuft (Prozess) und die Programmausgabe wird angezeigt, z. B.:
Hallo Welt!
- Editieren (edit): Eingabe des Programms
⇒ Quelltext (source code) - Compilieren (compile): Übersetzen des Quelltexts mit dem Compiler (
javac)
⇒ ein ausführbares Programm im Java-Bytecode - Ausführen (run): Ausführen des Programms auf der Java-VM (
java)
⇒ Programm läuft ab
$ vi HelloWorld.java
$ javac HelloWorld.java
$ java HelloWorld
HelloWorld
Bei Programmiersprachen, die compiliert werden, also einen Compiler benutzen, besteht die Programmentwicklung aus drei Schritten:
Zuerst muss das Programm in den Computer eingegeben werden. Man spricht hier vom editieren und das eingesetzte Werkzeug wird entsprechend als Editor bezeichnet. Nach dem Editieren liegt der Quelltext des Programms auf der Festplatte des Computers.
Da der Prozessor den Quelltext nicht ausführen kann, muss er erst vom Compiler in eine Form übersetzt werden, die der Computer ausführen kann. Hier gibt es bei Java eine Besonderheit, die es von anderen Programmiersprachen, wie z. B. C++ unterscheidet: Das Ergebnis der Compilation ist nicht ein Programm in der Maschinensprache des Prozessors, sondern der sogeannte Java-Bytecode. Der Grund hierfür ist, dass Java-Programme nicht direkt auf dem Prozessor des Computers ausgeführt werden, sondern auf einer virtuellen Maschine (VM) ablaufen. Der Bytecode ist somit die Maschinensprache er virtuellen Maschine. Bytecode hat seinen Namen von der Tatsache, dass die Maschinenbefehle der Java-VM nur ein Byte (8 Bit) lang sind.
Nachdem der Bytecode vom Compiler erstellt wurde, kann er von der virtuellen Maschine ausgeführt werden.
Java-VM
Java Programme laufen auf einer Virtuellen Maschine (Java-VM)
- bietet einen „virtuelle Prozessor“ auf dem die Java-Programme ausgeführt werden
- liest den Java-Bytecode ein
- übersetzt ihn in den Maschinencode des Prozessors
- lässt den Maschinencode ausführen
- ist selbst in C/C++ geschrieben
Vorteil
- neuer Prozessor/neues Betriebssystem ⇒ nur die Java-VM anpassen
- Java-Programme laufen unverändert überall
Wie bereits erwähnt, laufen Java-Programme nicht direkt auf dem Prozessor des Computers, sondern auf einer virtuellen Maschine. Die virtuelle Maschine verhält sich wie ein Interpreter, der die Instruktionen des Bytecodes ausführt und somit in die Maschinensprache des Prozessors konvertiert.
Moderne Java-VMs (z. B. die Oracle HotSpot-VM) arbeiten nicht als reiner Interpreter, sondern sind in der Lage, den Bytecode bei Bedarf in die Maschinensprache des Prozessors zu compilieren (just in time compilation). Hierdurch kann eine deutlich höhere Verarbeitungsgeschwindigkeit erreicht werden, als bei einem Interpreter. Da das Compilieren auch Zeit verbraucht, werden nur die häufig benutzten Programmteile (hot spots) übersetzt, der Rest wird interpretiert.
Der Grund für die Indirektion über eine Java-VM liegt darin, dass man hierdurch die Java-Programme unabhängig von der Hardware halten kann. Ein Java-Programm kann daher – vorausgesetzt es gibt eine passende VM – auf jeder beliebigen Hardware ausgeführt werden, ohne neu compiliert werden zu müssen. Da es Java-VMs für nahezu jeden Computer und viele Smartphones gibt, ist der Werbespruch von Java „Write once, run everywhere“ tatsächlich wahr.
Programmgerüst
Um ein erstes Java-Programm zu schreiben, muss man in eine Klasse anlegen und in dieser Klasse eine Methode mit dem Namen main anlegen. Der Name der Klasse ist beliebig, die Methode muss aber main heißen, sonst wird sie nicht gefunden.
Ein Java-Programm besteht immer aus einer Main-Klasse
- Name der Klasse kann frei gewählt werden
- einzelne Syntaxelemente können leider erst später erklärt werden
- vorläufig einfach nehmen, wie es ist
- das Java Programm gehört in die main-Methode
class NAME {
public static void main(String[] args) {
// Programm kommt hier hinein
}
}
Auch hier gibt es einige Konstrukte (class, public, static, []), die noch unbekannt sind. Diese werden im Laufe des Semesters erläutert und müssen vorläufig einfach hingenommen werden. Aus dem HelloWorld-Programm sollte aber bereits die main-Methode bekannt sein, die der Einstiegspunkt in das Java-Programm ist.
Ausgabeanweisung
- Die Ausgabeanweisung gibt Daten auf der Konsole aus
- Syntax:
System.out.println(WERT);
System.out.println("Hallo Java"); // -> Hallo Java
System.out.println(7); // -> 7
System.out.println(39 + 3); // -> 42
Leider können wir an dieser Stelle noch nicht erläutern, wie die Ausgabeanweisung aufgebaut ist, da die entsprechende Theorie noch fehlt. Bis dies nachgeholt werden kann, sollte die Anweisung einfach wie sie ist in das jeweilige Programm übernommen werden. Da es sich bei Java nicht um eine reine Lehrsprache (wie z. B. Pascal) handelt, müssen sich Anfänger:innen mit teilweise für sie unnötig komplizierten Konstrukten herumschlagen.
Eingabeanweisung
Die Eingabeanweisung liest Daten von der Konsole ein
- Importieren der Klasse
Scannerimport java.util.Scanner; - Anlegen einer Eingabe mit
Scanner scanner = new Scanner(System.in); - Lesen von Daten mit dem Scanner (abhängig von der Art der Daten)
- Ganzzahlen
int daten = scanner.nextInt(); - Fließkommazahlen
double daten = scanner.nextDouble(); - Zeichenketten
String daten = scanner.next();
Die Verwendung der Eingabeanweisung ist noch etwas komplizierter als die Ausgabeanweisung. Bevor sie verwendet werden kann, muss sie dem Programm erst durch eine Importanweisung import bekannt gemacht werden. Auch hier können wir die Theorie erst später nachreichen.
Beispiel: Eingabeanweisung
import java.util.Scanner;
public class ConsoleIO {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
double wert = scanner.nextDouble();
System.out.println("Sie gaben ein: " + wert);
}
}
Erstes Java-Programm: Voraussetzungen
- JDK (Java Development Kit)
https://jdk.java.net/ - Texteditor, z. B. Visual Studio Code
https://code.visualstudio.com/
Weitere Werkzeuge
Für den weiteren Verlauf der Vorlesung werden wir Eclipse als Entwicklungsumgebung einsetzen. Es handelt sich hierbei um eine professionelle Entwicklungsumgebung, sodass sie für den Einsteiger erschreckend umfangreich erscheint. Da es sich aber um die am weitesten verbreitete Entwicklungsumgebung handelt, werden wir sie – trotz des unnötig großen Funktionsumfangs – später in dieser Lehrveranstaltung einsetzen.