← Назад к вопросам

Как хранятся данные в Java

2.0 Middle🔥 81 комментариев
#Другое

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как хранятся данные в Java: память, структуры, сериализация

В Java данные хранятся несколькими способами в разных частях памяти и хранилищах. Это критическое знание для понимания производительности и управления ресурсами.

1. Память JVM: Stack и Heap

┌─────────────────────────────────────┐
│            STACK                    │
│ (Локальные переменные, ссылки)     │
│                                     │
│  Thread 1: [int age: 30]           │
│           [User ref: 0x1000]       │
│                                     │
│  Thread 2: [String name]           │
│           [List ref: 0x2000]       │
└─────────────────────────────────────┘
            ↓ ссылка
┌─────────────────────────────────────┐
│            HEAP                     │
│ (Объекты, массивы)                │
│                                     │
│  0x1000: User {age: 30, ...}      │
│  0x2000: ArrayList [1,2,3,...]    │
│  0x3000: String "Hello"            │
└─────────────────────────────────────┘

2. Примитивные типы vs. Объекты

// ПРИМИТИВНЫЕ ТИПЫ — хранятся в STACK (быстро)
int age = 30;              // 4 байта в stack
long salary = 50000L;      // 8 байт в stack
boolean active = true;     // 1 бит в stack
double rate = 2.5;         // 8 байт в stack

// ОБЪЕКТЫ — ссылка в STACK, данные в HEAP
User user = new User();    // ref в stack → объект в heap
String name = "John";      // ref в stack → String в heap
List<Integer> list = new ArrayList<>(); // ref в stack

// Размеры примитивов
byte: 1 байт    (−128 до 127)
short: 2 байта  (−32768 до 32767)
int: 4 байта    (−2^31 до 2^31-1)
long: 8 байт    (−2^63 до 2^63-1)
float: 4 байта  (IEEE 754)
double: 8 байт  (IEEE 754)
boolean: 1 байт (true/false, но занимает 1 байт)
char: 2 байта   (Unicode символ)

3. Объекты в памяти

public class User {
  private int age;          // 4 байта
  private String name;      // 8 байт (ссылка на 64-bit JVM)
  private boolean active;   // 1 байт (+ 7 байт padding)
  private double salary;    // 8 байт
}

// Размер объекта в памяти:
// Object header: 12 байт (или 16 с padding)
// age: 4 байта
// name ref: 8 байт
// active: 1 байт (+ 7 padding)
// salary: 8 байт
// = ~48 байт (зависит от JVM)

4. String пулинг

// String хранятся в специальном String Pool (в Heap)
String s1 = "hello";      // создаёт в String Pool
String s2 = "hello";      // переиспользует из Pool

System.out.println(s1 == s2); // true (одна ссылка)

String s3 = new String("hello"); // создаёт новый объект в Heap
System.out.println(s1 == s3);    // false (разные объекты)
System.out.println(s1.equals(s3)); // true (одинаковое содержимое)

// Явное добавление в пул
String s4 = s3.intern();  // теперь s4 в String Pool
System.out.println(s1 == s4); // true

5. Массивы

// Примитивные массивы
int[] numbers = new int[100];  // 400 байт (100 * 4) + header
boolean[] flags = new boolean[1000]; // 1000 байт + header

// Массивы объектов
User[] users = new User[10];   // 10 ссылок (80 байт на 64-bit) + header
// Каждый user — отдельный объект в Heap

// Многомерные массивы
int[][] matrix = new int[100][100];
// = массив из 100 ссылок на массивы из 100 int
// = рассчитанное использование памяти

6. Сборка мусора (Garbage Collection)

процесс жизни объекта в Heap:

1. ALLOCATION        Создание объекта
   new User()        → выделение памяти в Heap

2. REACHABLE        Объект доступен через ссылку
   User u = new User(); → u указывает на объект

3. UNREACHABLE      Нет ссылок на объект
   u = null;        → объект становится кандидатом на GC

4. GC                Сборка мусора
   System.gc();     → удаление неиспользуемых объектов
   (Вызывается автоматически)

7. Сериализация данных

// СЕРИАЛИЗАЦИЯ — преобразование объекта в байты
public class User implements Serializable {
  private int age;
  private String name;
  private static final long serialVersionUID = 1L;
}

// В файл
User user = new User(30, "John");
FileOutputStream fos = new FileOutputStream("user.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(user);
oos.close();

// Из файла
FileInputStream fis = new FileInputStream("user.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
User loaded = (User) ois.readObject();
ois.close();

8. JSON сериализация (Jackson)

import com.fasterxml.jackson.databind.ObjectMapper;

User user = new User(30, "John");

// Объект → JSON строка
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(user);
// {"age": 30, "name": "John"}

// JSON → Объект
User restored = mapper.readValue(json, User.class);

9. Хранение в БД

// RELATIONAL (SQL)
User table:
  id | name  | age | salary
  ---+-------+-----+--------
  1  | John  | 30  | 50000
  2  | Jane  | 25  | 60000

// In Java:
String query = "SELECT * FROM users WHERE id = ?";
PreparedStatement stmt = connection.prepareStatement(query);
stmt.setInt(1, userId);
ResultSet rs = stmt.executeQuery();

if (rs.next()) {
  User user = new User(
    rs.getInt("id"),
    rs.getString("name"),
    rs.getInt("age")
  );
}

// NOSQL (MongoDB)
Db.users.insertOne({
  "_id": ObjectId(),
  "name": "John",
  "age": 30,
  "salary": 50000
});

// In Java:
MongoCollection<Document> collection = db.getCollection("users");
Document doc = new Document("name", "John")
  .append("age", 30);
collection.insertOne(doc);

10. Cache структуры

// HashMap — хеширование с коллизиями
Map<String, User> users = new HashMap<>();
users.put("user1", new User(30, "John"));
// Хранит: hash("user1") → [User object]

// TreeMap — отсортирован по ключам
Map<String, User> sorted = new TreeMap<>(users);
// Использует Red-Black Tree структуру

// WeakHashMap — для кэша (объекты удаляются при GC)
Map<String, User> cache = new WeakHashMap<>();
user = null; // объект удалится из WeakHashMap

11. Memory Leaks — утечки памяти

// Плохо: сохранение ссылок
private static List<byte[]> list = new ArrayList<>();

public void loadData() {
  byte[] data = new byte[1000000];
  list.add(data); // data никогда не удалится!
}

// Хорошо: очистка
private List<byte[]> list = new ArrayList<>(); // не static

public void cleanup() {
  list.clear(); // может быть GC'd
}

// Слушатели событий
button.addClickListener(() -> doSomething());
// Нужно удалить слушателя:
button.removeClickListener(listener);

12. Оптимизация памяти

// Используй примитивы вместо Wrapper объектов
int[] array = new int[1000]; // лучше
Integer[] array = new Integer[1000]; // хуже (8 байт per int)

// Кэширование String
String s = "hello".intern(); // используй пул

// Избегай излишних копий
list.stream()
  .filter(...)
  .map(...)
  .collect(Collectors.toList()); // создаёт новый список

// Переиспользуй объекты
for (int i = 0; i < 1000000; i++) {
  User user = reuseUser; // переиспользовать
  user.setAge(i);
  process(user);
}

Итоги хранения данных

  • Stack — примитивные типы и ссылки (быстро, удаляется при выходе из scope)
  • Heap — объекты (медленнее, управляется GC)
  • String Pool — оптимизация для строк
  • Сериализация — преобразование в байты для файлов/сети
  • БД — долговременное хранилище
  • Кэши — быстрый доступ к данным
  • GC — автоматическое освобождение неиспользуемой памяти