kone
ainovel

ainovel

시마린 번역기 메모리 누수 코드베이스 분석 요청해봄

02/13/2026, 07:48:46
잡담
1481 views · 2 likes

요약하자면 

1. 누더기 프롬프트가 특정 구문에서 검열 필터를 자극했고
2. 해당 청크가 무한 분할하면서 메모리가 폭발함


물론 나도 버그 재현해본건 아니라 틀릴 수도 있음








시마린 번역기 메모리 누수 코드베이스 분석

커뮤니티에서 보고된 "번역율 68%/82%에서 메모리 폭증" 이슈에 대한 코드 분석 결과입니다.


분석 대상 파일

파일

역할

[main_translator.py]

메인 번역 루프 (2-Pass)

[translate_core.py]

Gemini API 호출 + Divide & Conquer

[epub.py]

EPUB 파일 로드/저장

[translatable_xhtml.py]

챕터별 HTML 파싱/번역 적용

[home_view_model.py]

번역 파이프라인 오케스트레이션


🔴 위험도 높음: 핵심 메모리 누수 후보

1. EPUB 파일 콘텐츠 3중 복제

[!CAUTION] EPUB의 전체 ZIP 콘텐츠가 메모리에 최소 3번 복제됩니다. 이미지가 많은 책일수록 영향이 큽니다.

[epub.py#L84-L93]에서 ZIP의 모든 파일(이미지 포함)을 self._contents에 로드합니다:

python
with zipfile.ZipFile(file_path, 'r') as zin:
    for name in zin.namelist():
        self._contents[name] = zin.read(name)  # 1차: 전체 ZIP 내용

그 후 [epub.py#L424-L437]의 _preserve_original_chapters에서 챕터별로 복제:

python
self._original_contents[chap] = self._contents[chap]  # 2차: 원본 보관
self._contents[new_path] = original_html.encode('utf-8')  # 3차: seamarine_originals/에 추가

그런데 _execute가 2번 호출됩니다 ([main_translator.py#L45-L55]

python
def run(self):
    self._execute(1)      # 1차: Epub 객체 생성 → 전체 메모리 로드
    time.sleep(10)
    self._execute(2)      # 2차: 또 다른 Epub 객체 생성 → 또 전체 메모리 로드

1차 _execute에서 생성된 book 객체가 명시적으로 해제되지 않으므로, 2차 실행 시 1차의 book이 아직 메모리에 남아있을 수 있습니다.

메모리 영향 예시:

  • EPUB 50MB → _contents + _original_contents + seamarine_originals/ ≈ ~100-150MB

  • 2차 _execute 시 추가 → 총 ~200-300MB


2. Divide & Conquer 재귀 깊이 미제한

[!WARNING] 특정 콘텐츠에서 API 오류가 반복되면 재귀적으로 JSON을 분할하여 메모리가 기하급수적으로 증가합니다.

[translate_core.py#L246-L313]

python
def _divide_and_conquer_json(self, contents, level=0, ...):
    json_dict = json.loads(contents)
    mid = len(keys) // 2
    first_half = {k: json_dict[k] for k in keys[:mid]}   # 새 dict
    second_half = {k: json_dict[k] for k in keys[mid:]}   # 새 dict
    
    # 재귀 — 깊이 제한 없음!
    subresp_1 = self.generate_content(subcontents_1, level=level+1, ...)
    subresp_2 = self.generate_content(subcontents_2, level=level+1, ...)
  • level 파라미터가 있지만 깊이 제한 조건이 없음

  • 400/500 에러 반복 시 계속 재귀 → 중간 결과가 스택에 누적

  • "누더기 프롬프트"가 이 경로를 빈번하게 트리거할 가능성이 높음


3. 번역 루프 내 반복적 dict 정렬/재생성

[main_translator.py#L166-L176]

python
for future in as_completed(futures):
    translated_text_dict.update(translated_chunk)
    translated_text_dict = dict(sorted(translated_text_dict.items()))  # 매번 새 dict!
    with open(..., "w") as f:
        json.dump(translated_text_dict, f)  # JSON 직렬화 중 추가 메모리
  • 매 청크 완료마다 전체 dict의 새 복사본 생성

  • 번역 후반부(68%, 82%)에서 dict가 커질수록 영향 증대


🟡 위험도 중간

4. 챕터별 BeautifulSoup DOM 전체 보존

python
# main_translator.py L116-L126
xhtmls: dict[str, utils.TranslatableXHTML] = {}
for chapter_file in chapter_files:
    xhtmls[chapter_file] = utils.TranslatableXHTML(...)  # 각각 BS4 DOM 보유

모든 챕터의 DOM이 동시에 메모리에 존재하며, get_translated_html()에서 추가로 DOM 전체를 복사합니다.

5. ThreadPoolExecutor Future 결과 누적

모든 청크를 한번에 submit하여 Future 객체와 완료된 결과가 as_completed로 소비되기 전까지 메모리에 누적됩니다.

6. genai Client 재생성

키 로테이션 시 genai.Client()를 새로 생성하며, 이전 Client의 내부 HTTP 연결 풀이 즉시 해제되지 않을 수 있습니다.


📊 커뮤니티 이슈 증상과의 매핑

커뮤니티 보고

코드 내 원인

위험도

68%에서 1차 폭증

#1 + #3: dict 정렬 비용 증가

🔴

82%에서 2차 폭증

#1: 2차 _execute 시 새 Epub 로드

🔴

누더기 프롬프트만 문제

#2: 복잡 프롬프트 → D&C 재귀 다발

🔴

같은 레이블 다른 책 OK

책 콘텐츠 특성이 D&C 유발 빈도 결정

🟡


💡 개선 제안 요약

  1. _execute 간 book 명시적 해제del book + gc.collect()

  2. D&C 깊이 제한level >= 5일 때 예외/빈 결과 반환

  3. dict 정렬은 루프 종료 후 1회만 수행

  4. EPUB 이미지 lazy loading: 번역 시 이미지는 메모리에 로드 불필요

  5. 챕터별 스트리밍 처리: 모든 DOM 동시 유지 방지

  6. Future batch 실행: 한번에 모든 chunk submit 대신 batch로 나눠 실행

2
0 comments
[포켓몬] 알로라 소년의 칼로스 여행 1-296
[패러디]
야돈
02/07 3210 21
[포켓몬] 악 타입 마스터의 길 1-400
[패러디]
야돈
02/07 2754 21
마블 로그인 보상 Marvel My Sign-in System 1~311
[패러디]
ekaros
02/07 2460 24
시골에 처박혀 있는데 내가 거물이라고? 1~839
[일반]
ascr156
02/07 3830 38
스피드 쌀먹 공유 모음 20260207
[일반]
tassdar
02/07 6627 98
금풍세우루 루주로 시작해서, 검 한자루로 천하제패 444-1100
[일반]
quer99
02/07 2517 29
[마블] 마블의 대만화가님 1-407
[패러디]
金日成綜合大學
02/07 3051 33
아침 쌀 ~~~~
[일반]
afjtj4bgjejwnf
02/07 4084 87
명일방주에서 산림감시원, 테라에서 주지육림 외 복구
[복구]
bebee
02/06 3140 27
26년 1월 치디엔 랭킹 소설 후기(읽은 것만)
후기
펭귄
02/06 5693 32
스포주의) [백세수선 : 내 재능은 계승할 수 있다] 보다가 뒤통수 맞았습니다...
잡담
tassdar
02/06 4039 19
LA명탐정 1~701(완)
[일반]
ascr156
02/06 3436 60
바쁘다 바빠
잡담
라스트오리진으로 팔려간 댕라
02/06 3690 18
[동방]환상향 파라다이스 프로젝트来自幻想乡的乐园计划 1-732
[패러디]
hyun6871
02/06 4112 79
여러분 추천보단 솔직히 댓글이 좋지않습니까
잡담
하이바라
02/06 3455 17
중국소설공유방 링크
정보
1atchup98fy
02/06 11387 21
60년대로 회귀 농사로 부자 되고 아이 키우기_1-701_重回六零:種田發家養崽崽
[복구]
serv-2004
02/06 2595 26
스피드 쌀먹 공유 모음 20260206
[일반]
tassdar
02/06 7063 99
성진지주 641장 1241화
[일반]
wlsdnr13
02/06 2966 40
금풍세우루의 루주로 시작해, 단 한 번의 칼질로 천하를 놀라게 하다 444- 950
[일반]
quer99
02/06 2196 25
우등생은 초능력이 필요없다 개드립성 후기 (스포주의)
후기
hyun6871
02/06 3500 17
[마블] 미국만화-내가 홈랜더다1-243
[패러디]
金日成綜合大學
02/05 3402 26
[동방]환상향에서 느긋한 일상来自幻想乡的悠哉日常 1~617 (완결) 복구
[복구]
hyun6871
02/05 2876 48
(누락 보충)[5등분x우공못x헨스키]우등생은 초능력이 필요없다优等生不需要超能力1-1220 (완결)
[패러디]
hyun6871
02/05 3297 65
스피드 쌀먹 공유 모음 20260205
[일반]
tassdar
02/05 6768 104
금풍세우루의 루주로 시작해, 단 한 번의 칼질로 천하를 놀라게 하다 444- 900
[일반]
quer99
02/05 2046 23
1988로 회귀해 목수부터 시작하다 1~1353(완)
[일반]
ascr156
02/05 3593 54
실력지상주의 교실의 일기장 1~300 완결 + 외전
[패러디]
라스트오리진으로 팔려간 댕라
02/05 2937 73
[포켓몬] 노력은 결코 헛되지 않으며, 기술은 무한히 업그레이드할 수 있습니다. 精靈:天道酬勤,招式無限升級1~487
[패러디]
ekaros
02/04 3886 24
[포켓몬] 포켓몬 세계에서 불로불사를 키우려면 어떻게 해야 하나요 精灵:你怎么在精灵世界修仙啊 1~489 완결
[패러디]
ekaros
02/04 2933 28