kone
ainovel

ainovel

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

02/13/2026, 07:48:46
잡담
1440 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-77
[패러디]
야돈
02/15 1580 15
[미드] 모던 패밀리의 일원이 되다 1-77
[패러디]
야돈
02/15 1693 15
[나루토] 휴우가의 운명의 눈 1-61
[패러디]
야돈
02/15 1754 20
[나루토] 천재 캐릭터 설정부터 시작한다 1-75
[패러디]
야돈
02/15 1963 26
[MLP] Different Royal Pony 1-100
[패러디]
야돈
02/15 2342 15
[DC] 대사건이 계속 나를 쫓아온다 1-106
[패러디]
야돈
02/15 2179 26
[미드] Young Sheldon_Reborn As Georgie 1-86
[패러디]
야돈
02/15 2244 30
설날 특집 비싼 쌀~~~
[일반]
afjtj4bgjejwnf
02/15 5490 124
조금 쌀먹해옴
일반
talsoul
02/14 3827 69
내가보려고 번역한 명일방주 패러디 모음 공유
[패러디]
명일방주너무좋아광인
02/14 3440 66
스피드 쌀먹 공유 모음 20260214
[일반]
tassdar
02/14 5553 94
금풍세우루 루주로 시작해서, 검 한자루로 천하제패 444-1450
[일반]
quer99
02/14 1938 21
사이버펑크에서 시작하는 차원 공학 1-1015(완)
[패러디]
kone1133
02/14 3687 45
공간 홍안 재밌다고 해서 읽어봤는데....? 하차후기
후기
hkrjjngndjsj1
02/14 3681 15
[다중]종합만화, 나는 인기 지상주의의 최전선에 선다(1-1524)
[패러디]
zeta21
02/14 3977 50
MyGO에 가입한 나는 살아있는 부처 1~270
[패러디]
라스트오리진으로 팔려간 댕라
02/14 3207 37
걸 아이돌은 착취 당해야지 1-454
[복구]
tassdar
02/13 3147 22
데릴사위부터 시작하는 장생 가문 1-990
[선협]
nannakhs11
02/13 12541 57
[뱅드림]오늘부터, 네가 새로운 토가와 사키코다从今天起,你就是新的丰川祥子 1-324
[패러디]
hyun6871
02/13 3178 33
스피드 쌀먹 공유 모음 20260213
[일반]
tassdar
02/13 5747 96
[역내청x사에카노]부인,결국 당신이 더 좋아太太,我果然还是更喜欢你啊加料 (완결)
[패러디]
hyun6871
02/13 3114 71
[원신]평범한 인간인 나는 너희와 마지막까지 함께할 수 없어
[복구]
rt7777
02/13 2309 22
[나루토] 나뭇잎 마을의 우즈마키 멘마1-394
[패러디]
金日成綜合大學
02/13 3579 19
코난 시체수집가 1~3712
[패러디]
aabbp
02/12 3989 75
엠페러 드래곤 1-200
[일반]
almost
02/12 2922 22
잠자기전 나머지 쌀!!!!!
[일반]
afjtj4bgjejwnf
02/12 4867 82
[우마무스메]우마무스메,게임 속으로赛马娘,身在游戏⊙1-800
[패러디]
hyun6871
02/12 3232 66
[뱅드림]MyGO!!! 내가 서큐버스 사키코라고?MyGO!!!我当魅魔小祥?1-110
[패러디]
hyun6871
02/12 1962 20
스피드 쌀먹 공유 모음 20260212
[일반]
tassdar
02/12 6140 100
금풍세우루 루주로 시작해서, 검 한자루로 천하제패 444-1300
[일반]
quer99
02/12 1470 17