12 메모리

12.1 개요

(1) 상태의 모든 저장소는 호스트가 준 덩이 안에 있고(5.1), 적재 때 자리를 잡는다. 호스트가 빌려주고 보증하는 메모리(12.1 (2))만 예외다. 돌 때 새 저장소가 생기지 않는다. 쓰레기 수거기가 없고, 값이 다른 값을 주소로 가리키지 않는다.

(2) 값이 놓이는 자리는 넷이다.

(3) 따라서 프로그램이 쓰는 메모리의 위쪽 한계는 적재 때 알 수 있다. 구역을 다 쓰거나 용량을 넘는 일은 돌 때 결함이나 실패로 드러나지, 처리기가 메모리를 더 청하는 것으로 이어지지 않는다.

12.2 구역과 표시

(1) 새 바이트를 만드는 낱말 — cat, str.upper, str.repeat, 숫자를 글로 바꾸는 낱말 등 — 은 결과를 구역에 쓰고 그 뷰를 준다. 구역은 앞에서부터 차례로 채워진다. 낱말 region_free 는 남은 바이트 수를 준다.

(2) mark 는 구역의 지금 자리를 owned 값(타입 mark)으로 준다. 표시 release 는 구역을 그 자리로 되돌린다. 표시 뒤에 만든 바이트는 모두 다시 쓸 수 있게 된다. 많은 글을 만드는 루프는 바퀴마다 표시하고 되돌린다.

(3) 저장된 글(str(N))에 넣은 바이트는 그 자리로 복사되므로 구역을 되돌려도 남는다.

12-region.pnx

use io
use str
global kept str(32)
word line (i int gives str) cat ("line number ", i) end
word demo ()
  region_free to start
  1 line to a                                 rem a view of 13 new bytes in the region
  a set kept                                   rem stored text: the bytes are copied out
  start region_free sub to one
  mark to m
  2 1000 range do (i int) i line drop end each
  start region_free sub to many
  m release                                   rem back to the place of the mark
  start region_free sub to after
  cat ("one line: ", one, ", 999 lines: ", many, ", after release: ", after) io.println
  kept io.println
end
demo

출력

$ pnx 12-region.pnx
one line: 13, 999 lines: 14877, after release: 13
line number 1

(4) 호스트가 받은 str 결과는 그 상태에 대한 다음 호출 전까지만 쓸 수 있다. 오래 쓸 글은 호스트가 제 메모리로 복사한다(19절).

결함

(5) 구역이 모자라면 결함 full 이다.

12-region-full.pnx

word fill ()
  loop cat ("this line takes some bytes of the region ", 0) drop end
end
fill

결함으로 멈춤

$ pnx 12-region-full.pnx
12-region-full.pnx:2: a declared capacity is used up

12.3 뷰의 수명

(1) 구역을 가리키는 뷰는 그 바이트가 되감기면 쓸 수 없다. 처리기는 이것을 적재 때 검사한다. 한 몸 (낱말, 값이 되는 블록, 정의 밖의 코드) 안에서 str 값마다 그 앞에 만든 표시의 수를 센다. 표시 release 는 그 표시 뒤에 만든 값을 거둔다.

제약

(2) 거둘 값이 스택에 남아 있으면 release 가 거절된다.

12-release-stack.pnx

word f (gives str)
  mark to m
  cat ("a", "b")
  m release
end

거절

$ pnx 12-release-stack.pnx
12-release-stack.pnx:4:5: a str made after this mark is still on the stack; the release gives its bytes back

(2a) 낱말이 실패해서(or_fail, fail; 10.7 (1)) 도는 미룬 코드 안의 release 는 낱말이 스택에 남기는 값을 세지 않는다. 낱말은 거기서 끝나고, 그 스택은 다시 읽히지 않으며, 미룬 코드는 거기서 아무것도 가져가지 않는다(10.7 (4)). 미룬 코드 자신의 값은 (2)처럼 세고, 거둔 값을 든 지역 변수는 (3)과 같다. 실패는 코드만 나르므로(11.2) 그 길로 낱말을 떠나는 str 은 없다.

12-release-failing.pnx

use io
use str
word check (w str or fail)
  w str.len 5 gt if err.range fail end
end
word words_of (line str or fail)
  loop
    mark to m
    defer m release end
    line str.lower str.words do (w str) w check or_fail  w io.println end each
    break
  end
end
"Pinax interprets" words_of or_else (e) cat ("failed: ", e err_text) io.println end

출력

$ pnx 12-release-failing.pnx
pinax
failed: range

(3) 거둔 값을 든 지역 변수는 다시 넣기 전까지 읽지 못한다.

12-release-view.pnx

use io
word f ()
  mark to m
  cat ("made after ", "the mark") to s
  m release
  s io.println
end

거절

$ pnx 12-release-view.pnx
12-release-view.pnx:6:3: this str was made after a mark that was released since; the region may hold other bytes there now

(4) 이 검사는 조심스러운 쪽으로 기운다. 리터럴의 문자열은 구역에 없으므로 거두지 않는다. 그 밖의 str — 뷰, 저장된 글을 읽은 것, 부른 낱말이 준 것 — 은 모두 새로 만든 것으로 센다. 갈래가 만나면 값은 가장 늦게 만든 것으로, 표시는 가장 이른 것으로 합친다. 그래서 맞는 프로그램을 거절할 수 있다. 그럴 때는 값을 str(N) 에 넣어 두거나 표시의 자리를 옮긴다.

(5) owned 표시를 낱말에 넘기면 넘기는 자리를 release 로 본다. 표시를 받는 낱말은 자기 str 입력을 그 표시 뒤에 만든 것으로 본다. 두 작업이 같은 구역을 쓰는 경우는 이 검사가 보지 않는다(14절).

12.4 한 번만 쓰는 값

(1) 타입 앞의 owned 는 한 번만 쓰는 값(owned value)을 뜻한다. 열린 파일 같은 핸들, 풀에서 얻은 줄, 구역의 표시가 그렇다. 처리기는 그런 값이 모든 경로에서 정확히 한 번 넘겨지는지 적재 때 검사한다.

(2) owned 값을 쓰는 길은 둘이다.

(3) owned 값은 지역 변수에 넣거나 바로 넘겨줘야 한다. dup 으로 늘리거나 drop 으로 버리지 못한다. 실패의 경로(or_fail or_return)와 return 의 자리에서도 넘겨줘야 한다.

12-owned-file.pnx

use io
use file
word write_note (path str or fail)
  path file.open_write or_fail to f            rem an owned file: it must be closed on every path
  "a note" f file.write or_else (e) f file.close e fail end
  f file.close
end
word read_note (path str gives s str or fail) path file.read_all or_fail end
"note.txt" write_note or_else (e) "cannot write" io.println end
"note.txt" read_note or_else (e) "cannot read" end io.println

출력

$ pnx 12-owned-file.pnx
a note
제약

(4) 넘겨준 값을 다시 쓰면 거절된다.

12-owned-twice.pnx

use file
word f (path str or fail)
  path file.open_read or_fail to h
  h file.close
  h file.close
end

거절

$ pnx 12-owned-twice.pnx
12-owned-twice.pnx:5:3: this local gave its owned value away; it holds nothing to read

(5) owned 값을 늘리거나 버리면 거절된다.

12-owned-dup.pnx

use file
word f (path str or fail)
  path file.open_read or_fail dup file.close file.close
end

거절

$ pnx 12-owned-dup.pnx
12-owned-dup.pnx:3:31: an owned value is kept in a local or given on at once; this word would lose it

(6) 어느 한 경로라도 넘겨주지 않으면 거절된다.

12-owned-path.pnx

use file
word f (path str, quick bool or fail)
  path file.open_read or_fail to h
  quick if return end
  h file.close
end

거절

$ pnx 12-owned-path.pnx
12-owned-path.pnx:4:12: an owned value in a local is not given on every path (close, free or store it)

(7) 결함으로 끝난 실행에서는 처리기가 그 실행이 연 핸들을 닫는다(11.3 (3)).

진단
조건메시지
release 할 때 거둘 값이 스택에 있음a str made after this mark is still on the stack; the release gives its bytes back
거둔 값을 읽음this str was made after a mark that was released since; the region may hold other bytes there now
넘겨준 값을 다시 읽음this local gave its owned value away; it holds nothing to read
owned 값을 늘리거나 버림an owned value is kept in a local or given on at once; this word would lose it
넘겨주지 않는 경로an owned value in a local is not given on every path (close, free or store it)