← Назад к вопросам
Где хранятся данные при выполнении запроса?
2.0 Middle🔥 161 комментариев
#Базы данных и SQL
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Где хранятся данные при выполнении запроса в Java
В Java данные во время выполнения запроса распределяются между несколькими местами памяти. Понимание этого критично для оптимизации и отладки.
JVM Memory Layout
Общая структура памяти JVM:
┌─────────────────────────────────────────┐
│ HEAP (Кучa) │
│ Совместно для всех потоков │
│ - Объекты │
│ - Arrays │
│ - Strings │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ STACK (Стек) × N потоков │
│ Отдельный для каждого потока │
│ - Примитивные типы │
│ - Ссылки на объекты │
│ - Локальные переменные │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ CODE (Код программы) │
│ - Bytecode методов │
│ - Константы │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ METHOD AREA / METASPACE │
│ - Определения классов │
│ - Код методов │
│ - Константы │
└─────────────────────────────────────────┘
Пример: Где хранятся данные при вызове метода
public class RequestHandler {
// ← Это определение класса хранится в METASPACE
public void handleRequest(String requestData) {
// requestData (ссылка на String) → STACK
// String объект сам → HEAP
int userId = 123; // примитив → STACK
User user = userService.findById(userId);
// user (ссылка) → STACK
// User объект → HEAP
List<Order> orders = new ArrayList<>();
// orders (ссылка) → STACK
// ArrayList объект → HEAP
// Order объекты → HEAP
}
// По выходу: все данные со STACK удаляются
// Объекты на HEAP остаются (пока есть ссылки)
}
1. STACK (Stack Memory)
Это LIFO (Last In First Out) структура, отдельная для каждого потока:
Что там хранится:
- Примитивные типы (int, boolean, double)
- Ссылки на объекты (не сами объекты!)
- Информация о вызовах методов (stack frames)
- Локальные переменные
public void example() {
int x = 10; // ← STACK: x = 10
double y = 3.14; // ← STACK: y = 3.14
String name = "John"; // ← STACK: ссылка на String
User user = new User(); // ← STACK: ссылка на User
method2(); // ← STACK FRAME для method2 добавляется сверху
} // ← По выходу все переменные удаляются (pop)
Преимущества Stack:
- Очень быстрое выделение (просто сдвиг указателя)
- Очень быстрое удаление (просто сдвиг указателя)
- NO GC нужен
- Поток-безопасно (каждому потоку свой stack)
Ограничения Stack:
- Размер ограничен (обычно 1-8 MB на поток)
- Данные удаляются при выходе из метода
- StackOverflowError при рекурсии
public int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1); // ← Каждый вызов добавляет frame на STACK
}
// factorial(100000) → StackOverflowError!
// Слишком много frame'ов на stack'е
2. HEAP (Куча)
Общая память для всех потоков, управляется сборщиком мусора:
Что там хранится:
- Все объекты (new Object())
- Все массивы
- Все String'и (если не в string pool)
- Collections
- Данные, которые нужны долго
public void allocateData() {
// Все эти объекты → HEAP
User user = new User("John");
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
Map<Integer, String> map = new HashMap<>();
map.put(1, "value");
}
// Объекты остаются на HEAP, ссылки удаляются со STACK
// GC позже очистит неиспользуемые объекты
Преимущества Heap:
- Неограниченный размер (почти)
- Данные сохраняются пока есть ссылки
- Глобально доступно для всех потоков
Ограничения Heap:
- Медленнее Stack
- Требует GC для очистки
- Race conditions возможны
Типичная картина памяти при HTTP запросе
@RestController
public class UserController {
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
// STACK (поток обработки запроса):
// - id (Long ссылка)
// - request параметры
// - localsthis reference
User user = userService.findById(id);
// ↑ STACK: user (ссылка)
// ↑ HEAP: User объект + все поля
List<Order> orders = orderService.findByUser(user);
// ↑ STACK: orders (ссылка)
// ↑ HEAP: ArrayList + Order объекты
// При сериализации в JSON:
// Jackson создаёт байт-массив на HEAP
return user; // JSON ответ
// По завершении метода:
// - STACK frame удаляется
// - На HEAP остаются объекты (могут быть кэшированы)
// - GC удалит если нет ссылок
}
}
String Pool - специальный случай
String s1 = "hello"; // HEAP в string pool
String s2 = "hello"; // s2 указывает на тот же объект
String s3 = new String("hello"); // новый объект на HEAP
s1 == s2 // true, одна ссылка
s1 == s3 // false, разные объекты
s1.equals(s3) // true, одинаковое содержимое
// Оптимизация:
String s4 = s3.intern(); // s4 теперь указывает на string pool версию
Метаспейс / PermGen - определения классов
// Когда ты загружаешь класс:
public class User { ... }
// Его определение хранится в METASPACE (Java 8+)
// или PermGen (Java 7 и ниже)
// METASPACE содержит:
// - Bytecode методов
// - Поля класса (тип и имя)
// - Константы
// - Статические переменные
// Статические переменные:
public static class Counter {
static int count = 0; // ← METASPACE/PermGen
}
Memory Regions во время обработки запроса
Запрос пришёл:
├─ HTTP Request объект → HEAP
├─ RequestServlet → METASPACE
├─ Handler методы → METASPACE (bytecode)
└─ Поток обработки:
├─ STACK (размер 1MB):
│ ├─ request ссылка
│ ├─ params
│ └─ локальные переменные
└─ HEAP:
├─ User объекты
├─ Collections
├─ JSON response buffer
└─ ...
Ответ отправлен:
├─ STACK очищается
└─ HEAP объекты живут дальше (GC удалит позже)
Практический пример: анализ памяти
public class MemoryExample {
static List<String> globalList = new ArrayList<>(); // METASPACE (ссылка)
public static void process(String data) {
// Локальные переменные → STACK текущего потока
int count = 0; // STACK
String message = "Processing"; // STACK (ссылка)
// Объекты → HEAP
List<String> localList = new ArrayList<>(); // STACK (ссылка), объект → HEAP
localList.add(data); // data добавлен на HEAP
globalList.add(data); // также на HEAP, но ссылка в METASPACE
}
// STACK очищается: count, message, localList (ссылка)
// HEAP: объекты остаются (data есть в globalList!)
}
OutOfMemoryError - какой тип
// StackOverflowError - заполнен STACK
recursion(recursion(recursion(...))) // слишком глубоко
// OutOfMemoryError: Java heap space - заполнен HEAP
List<byte[]> list = new ArrayList<>();
while (true) {
list.add(new byte[1024 * 1024]); // 1MB каждый раз
}
// OutOfMemoryError: Metaspace - слишком много классов
// Обычно при динамической генерации классов
for (int i = 0; i < 1000000; i++) {
loadDynamicClass("Class_" + i);
}
Оптимизация: куда положить данные
// ✅ Примитивы на STACK если возможно
public void good() {
int x = 10; // STACK
double y = 3.14; // STACK
}
// ❌ Оборачивание примитивов в объекты
public void bad() {
Integer x = 10; // HEAP (autoboxing)
Double y = 3.14; // HEAP (autoboxing)
}
// ✅ Переиспользование объектов
StringBuilder sb = new StringBuilder(); // создать один раз
for (int i = 0; i < 1000; i++) {
sb.append(i).append(","); // переиспользовать
}
// ❌ Создание новых объектов в цикле
for (int i = 0; i < 1000; i++) {
String result = "value" + i; // новый String каждый раз → HEAP
}
Резюме
STACK:
- Примитивные типы и ссылки
- Локальные переменные метода
- Очень быстро, no GC
- Ограничен размером
- Очищается при выходе из метода
HEAP:
- Все объекты и массивы
- Глобально доступно
- Управляется GC
- Медленнее, но больше
- Существует пока есть ссылки
METASPACE:
- Определения классов
- Статические переменные
- Bytecode методов
- Не очищается GC (только при выгрузке класса)
Понимание этого — ключ к написанию эффективного Java кода и отладке проблем с памятью.