개선(improvement) #2627
open개선(improvement) #2310: [BE] Optimize Memory, CPU API search list flight
[BE] CRITICAL-3: Repeated Deep Copy Operations
0%
Description
Parent Issue: #2310 [BE] Optimize Memory, CPU API search list flight
- Summary
Multiple `deepCopy()` operations throughout the service creating 100-500 KB garbage per request, triggering excessive GC pressure under concurrent load.
- Location
File: `web-api/src/main/java/com/ohmy/api/service/flight/FlightListItineraryService.java`
All deepCopy() Locations:
| Line | Context | Impact per Call |
| ------ | --------- | ----------------- |
| 585 | `alliance.deepCopy()` in triple nested loop | 5-10 KB × loop iterations |
| 1162 | `fareJsonNode.deepCopy()` in `onewayFaresToFlightsFare()` | 2-5 KB per fare |
| 1242 | `x.deepCopy()` in stream map | 2-5 KB per item |
| 1543, 1548, 1597, 1620 | Multiple deepCopy in `getSelectedJsonNodeCombinedFares()` | 10-20 KB per call |
| 1683, 1760, 1793 | Multiple deepCopy in `getSelectedJsonNodeOnewayFares()` | 10-20 KB per call |
| 1999 | `flightJsonNode.deepCopy()` in `makeFareRequestBody()` | 5-10 KB |
| 2792 | `requestBody.get("condition").deepCopy()` in `getListItinerary()` | 1-2 KB |
- Problem Code Examples
// Line 585: Deep copy in triple nested loop alliance.deepCopy() // Line 1162: Deep copy in fare transformation JsonNode copiedFareJsonNode = fareJsonNode.deepCopy(); // Lines 1543-1548: Multiple deep copies flightFareJsonNode = flightJsonNode.deepCopy(); // ... more processing ... flightFareJsonNode = flightFareJsonNode.deepCopy(); // Line 2792: Deep copy in request processing ObjectNode conditionJsonNode = requestBody.get("condition").deepCopy();
- Performance Impact
Memory Allocation:
- Total per request: 100-500 KB garbage objects
- Under 50 concurrent requests: 5-25 MB additional garbage/second
- Each deepCopy() recursively copies entire JSON tree structure
GC Pressure:
- Triggers Young GC every 2-3 seconds under load
- Contributes to Full GC cycles when Old Gen fills
- Part of the GC death spiral causing OutOfMemoryError
CPU Impact:
- Object allocation overhead
- Recursive tree traversal for each copy
- GC CPU time increases proportionally
- Required Fix
- 1. Use Immutable Patterns
Instead of defensive copying, design methods to not modify input:// BEFORE: defensive copy JsonNode modified = original.deepCopy(); ((ObjectNode) modified).put("field", value); // AFTER: create new node with specific changes ObjectNode modified = JsonNodeFactory.instance.objectNode(); original.fields().forEachRemaining(entry -> { if (!entry.getKey().equals("field")) { modified.set(entry.getKey(), entry.getValue()); } }); modified.put("field", value);
```
- 2. Copy Only Modified Fields
Build new nodes with only changed fields instead of full tree copy:
```java
// BEFORE: full deep copy
JsonNode copy = fareJsonNode.deepCopy();
((ObjectNode) copy).put("itemCode", newValue);
// AFTER: selective copy
ObjectNode fare = JsonNodeFactory.instance.objectNode();
fare.setAll((ObjectNode) fareJsonNode);
fare.put("itemCode", newValue);
```
- 3. In-Place Mutation Where Safe
Use ObjectNode mutation when the object is not shared:// If fareJsonNode is owned by this method only ((ObjectNode) fareJsonNode).put("field", value); // No copy needed
- 4. Analyze Copy Necessity
Audit each deepCopy() to determine:
- Is this protecting against mutation that actually occurs?
- Can we refactor to avoid mutation entirely?
- Is this node shared across threads or requests?
- Acceptance Criteria
✅ MUST maintain 100% identical API response (no logic changes)
✅ Reduce garbage allocation from 100-500 KB to < 50 KB per request
✅ Eliminate deepCopy() in hot paths (triple nested loops)
✅ Keep only deepCopy() calls that are truly necessary for thread safety
✅ Verify GC reduction with profiling tools (VisualVM, JFR)
✅ Measure GC pause time improvement under 50 concurrent requests
- Constraints
⚠️ NO LOGIC CHANGES ALLOWED - This is a performance optimization only
- Do not modify calculation results or data transformations
- Do not alter API response structure or values
- Maintain thread safety where required
- Related Issues
- #2610 - CRITICAL-1: Excessive JsonNode Traversal (Fixed ✅)
- #2611 - AtomicReference Memory Retention (Fixed ✅)
- #2626 - CRITICAL-2: O(n³) Nested Loop Anti-Pattern
- Part of Phase 3: Major Refactoring in optimization roadmap
- Reference
See `docs/flight-list-itinerary/VERIFIED_ISSUES.md` - CRITICAL-3 section for complete analysis.
- Testing Requirements
1. Before/after memory profiling with identical request load
2. JFR recording comparing GC activity
3. Response validation: byte-for-byte comparison of API output
4. Load test: 50 concurrent requests, measure GC pause time